> For the complete documentation index, see [llms.txt](https://docs.trezalabs.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.trezalabs.com/developers/rest-api.md).

# Rest API

The Treza Platform REST API (v2.4.0) gives you programmatic control over hardware-isolated Trusted Execution Environments (TEEs): deploy and manage AWS Nitro Enclaves, retrieve cryptographic attestation documents, submit zero-knowledge proofs, store encrypted PII, redact PII from LLM traffic, and manage plans and billing — all over HTTPS with JSON.

## Base URL

All examples on this page use the production base URL:

```
https://app.trezalabs.com
```

A development server runs at `http://localhost:3000` when running the platform locally. Every endpoint lives under `/api` and accepts and returns `application/json` unless noted otherwise.

## Authentication

The API supports two authentication modes.

### 1. Privy bearer token (dashboard sessions)

Control-plane endpoints — enclaves, tasks, API keys, proxies, billing, account, and usage — authenticate with the Privy access token issued for your dashboard session:

```bash
curl "https://app.trezalabs.com/api/enclaves?account=user@company.com" \
  -H "Authorization: Bearer <privy-access-token>"
```

Many of these endpoints also accept an `account` (or `identifier`) query parameter as a fallback identifier when no bearer token is supplied — intended for development use.

### 2. Treza API keys

Programmatic access uses a Treza API key, created via [`POST /api/api-keys`](#create-api-key), sent as a bearer token:

```bash
curl https://app.trezalabs.com/api/redact/run \
  -H "Authorization: Bearer treza_live_..." \
  -H "Content-Type: application/json" \
  -d '{"text": "Email john@example.com about the meeting"}'
```

Each key carries scoped permissions that gate access per endpoint:

| Scope                                                                           | Grants                                           |
| ------------------------------------------------------------------------------- | ------------------------------------------------ |
| `enclaves:read` / `enclaves:write`                                              | Read / manage enclaves                           |
| `tasks:read` / `tasks:write`                                                    | Read / manage tasks                              |
| `logs:read`                                                                     | Read enclave logs                                |
| `pii:ingest`, `pii:write`, `pii:read`, `pii:delete`, `pii:consent`, `pii:audit` | PII vault operations                             |
| `redact:run`                                                                    | Text redaction and redaction-enclave attestation |
| `redact:proxy`                                                                  | OpenAI-compatible redacting chat completions     |
| `redact:log`                                                                    | Redaction audit log                              |

PII endpoints additionally require the `x-treza-account` header (legacy `x-treza-wallet`) matching the key owner. A Privy access token may be used instead of an API key on PII endpoints, granting full PII permissions over the caller's own data.

{% hint style="info" %}
**Legacy aliases.** The API was historically wallet-keyed. The `account` query parameter still accepts the legacy alias `wallet`, the `account` body field accepts `walletAddress`, and the `x-treza-account` header accepts `x-treza-wallet`. New integrations should use the `account` forms; an account identifier can be an email or a wallet address.
{% endhint %}

### Plan-gated endpoints

Some endpoints are gated by your subscription plan. When a limit or feature gate is hit, the API returns `402` (or `403` for feature checks on some endpoints) with a `code` of `PLAN_LIMIT` or `PLAN_FEATURE`:

* `POST /api/api-keys` — `402` when no active subscription or trial exists
* `POST /api/redact/chat/completions` — `402` `PLAN_LIMIT` when the plan's request limit is reached
* `POST /api/proxies` — `402` `PLAN_FEATURE` / `PLAN_LIMIT` (non-OpenAI providers, custom policies, proxy count)
* `PUT /api/proxies` — `402` `PLAN_FEATURE` (custom redaction policies require Pro or higher)
* `GET /api/redact/attest` — `403` `PLAN_FEATURE` (hardware attestation requires Enterprise)
* `GET /api/usage/export` — `403` `PLAN_FEATURE` (audit export requires Pro or Enterprise)

### Unauthenticated endpoints

`GET /api/billing/plans` and `POST /api/contact` require no authentication. `POST /api/billing/webhook` is authenticated by Stripe's signature header and is not intended for direct client use. `POST /api/kyc/proof` is an open endpoint protected by cryptographic proof validation.

## Errors

Errors use a consistent shape:

```json
{
  "error": "Error message",
  "details": ["Additional error details (optional)"]
}
```

Plan-gated failures additionally include `"code": "PLAN_LIMIT"` or `"code": "PLAN_FEATURE"`.

## Endpoint groups

| Group                                     | Endpoints                                                                    |
| ----------------------------------------- | ---------------------------------------------------------------------------- |
| [Enclaves](#enclaves)                     | CRUD, lifecycle, logs, PCRs, attestation, ANS, ASE, PII processor validation |
| [Providers](#providers)                   | Available enclave providers                                                  |
| [Docker](#docker)                         | Docker Hub search and tags                                                   |
| [GitHub Integration](#github-integration) | OAuth flow, repositories, branches                                           |
| [Build](#build)                           | GitHub → Docker image builds (CodeBuild)                                     |
| [API Keys](#api-keys)                     | Key management with scoped permissions                                       |
| [Tasks](#tasks)                           | Scheduled tasks within enclaves                                              |
| [KYC](#kyc)                               | ZK KYC proofs and the KYC → PII bridge                                       |
| [ZKVerify](#zkverify)                     | Proof submission, VK registration, aggregation                               |
| [ZKPassport](#zkpassport)                 | TEE-attested identity verification                                           |
| [PII](#pii)                               | Encrypted PII vault, consent, audit                                          |
| [Redaction Proxy](#redaction-proxy)       | Text redaction and redacting chat completions                                |
| [Proxies](#proxies)                       | Redaction proxy configuration                                                |
| [Billing](#billing)                       | Plans, checkout, portal, subscription, webhook                               |
| [Account](#account)                       | Account bootstrap                                                            |
| [Usage & Reports](#usage-and-reports)     | Usage insights, audit export, ASE reports                                    |
| [Contact](#contact)                       | Contact form                                                                 |

***

## Enclaves

Manage the full lifecycle of secure enclaves. Enclave `status` values: `PENDING_DEPLOY`, `DEPLOYING`, `DEPLOYED`, `PAUSING`, `PAUSED`, `RESUMING`, `PENDING_DESTROY`, `DESTROYING`, `DESTROYED`, `FAILED`.

### List enclaves

`GET /api/enclaves`

Retrieve all enclaves associated with an account.

| Query parameter | Required | Description                                                   |
| --------------- | -------- | ------------------------------------------------------------- |
| `account`       | Yes      | Account identifier to filter enclaves (legacy alias `wallet`) |

```bash
curl "https://app.trezalabs.com/api/enclaves?account=user@company.com" \
  -H "Authorization: Bearer <privy-access-token>"
```

**Response** `200`

```json
{
  "enclaves": [
    {
      "id": "enc_123456",
      "name": "Trading Bot Enclave",
      "description": "Secure environment for automated trading strategies",
      "status": "DEPLOYED",
      "region": "us-east-1",
      "account": "user@company.com",
      "providerId": "aws-nitro",
      "providerConfig": { "dockerImage": "my-app:latest", "cpuCount": 2, "memoryMiB": 512 },
      "createdAt": "2024-01-15T00:00:00Z",
      "updatedAt": "2024-01-15T00:00:00Z",
      "githubConnection": { "isConnected": true, "username": "example-user", "selectedRepo": "example-user/trading-bot", "selectedBranch": "main" },
      "error_message": "Deployment failed: Instance type not compatible with Nitro Enclaves"
    }
  ]
}
```

`error_message` is present only when an enclave failed. Responses also include the deprecated `walletAddress` field (alias of `account`) for older clients.

### Create enclave

`POST /api/enclaves`

Create a new secure enclave.

| Body field         | Required | Description                                                                                                  |
| ------------------ | -------- | ------------------------------------------------------------------------------------------------------------ |
| `name`             | Yes      | Display name                                                                                                 |
| `description`      | Yes      | Description                                                                                                  |
| `region`           | Yes      | Deployment region, e.g. `us-east-1`                                                                          |
| `account`          | Yes      | Account identifier that owns the enclave (legacy `walletAddress`)                                            |
| `providerId`       | Yes      | Provider ID, e.g. `aws-nitro`                                                                                |
| `providerConfig`   | No       | Provider-specific configuration (e.g. `dockerImage`, `cpuCount`, `memoryMiB`)                                |
| `githubConnection` | No       | GitHub connection (`isConnected`, `username`, `selectedRepo`, `selectedBranch`, `accessToken`)               |
| `sourceType`       | No       | `registry` (default), `github`, or `private-registry`                                                        |
| `privateRegistry`  | No       | Private registry credentials (`registryUrl`, `username`, `password`) when `sourceType` is `private-registry` |

{% hint style="info" %}
`sourceType: "github"` queues a CodeBuild Docker image build before deployment and requires `githubConnection.selectedRepo`. `sourceType: "private-registry"` stores the registry credentials securely server-side.
{% endhint %}

```bash
curl -X POST https://app.trezalabs.com/api/enclaves \
  -H "Authorization: Bearer <privy-access-token>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Trading Bot Enclave",
    "description": "Secure environment for automated trading strategies",
    "region": "us-east-1",
    "account": "user@company.com",
    "providerId": "aws-nitro",
    "providerConfig": { "dockerImage": "my-app:latest", "cpuCount": 2, "memoryMiB": 512 }
  }'
```

**Response** `201` — `{ "enclave": { ... } }` with the created enclave in status `PENDING_DEPLOY`. `400` on missing fields or invalid provider configuration.

### Update enclave

`PUT /api/enclaves`

Update an existing enclave.

| Body field                                                                          | Required | Description                                                               |
| ----------------------------------------------------------------------------------- | -------- | ------------------------------------------------------------------------- |
| `id`                                                                                | Yes      | Enclave ID                                                                |
| `account`                                                                           | Yes      | Account identifier (legacy `walletAddress`)                               |
| `name`, `description`, `region`, `providerId`, `providerConfig`, `githubConnection` | No       | Fields to update                                                          |
| `sourceType`                                                                        | No       | Deployment source to switch to (`registry`, `github`, `private-registry`) |

```bash
curl -X PUT https://app.trezalabs.com/api/enclaves \
  -H "Authorization: Bearer <privy-access-token>" \
  -H "Content-Type: application/json" \
  -d '{ "id": "enc_123456", "account": "user@company.com", "name": "Updated Enclave Name" }'
```

**Response** `200` — `{ "enclave": { ... } }`. `404` if the enclave is not found or access is denied.

### Delete enclave

`DELETE /api/enclaves`

Delete an existing enclave.

| Query parameter | Required | Description                                                  |
| --------------- | -------- | ------------------------------------------------------------ |
| `id`            | Yes      | Enclave ID to delete                                         |
| `account`       | Yes      | Account identifier for authorization (legacy alias `wallet`) |

```bash
curl -X DELETE "https://app.trezalabs.com/api/enclaves?id=enc_123456&account=user@company.com" \
  -H "Authorization: Bearer <privy-access-token>"
```

**Response** `200` — `{ "message": "Enclave deleted successfully" }`.

### Get a specific enclave

`GET /api/enclaves/{id}`

Retrieve details for a specific enclave by ID.

```bash
curl https://app.trezalabs.com/api/enclaves/enc_123456 \
  -H "Authorization: Bearer <privy-access-token>"
```

**Response** `200` — `{ "enclave": { ... } }`. `404` if not found.

### Manage enclave lifecycle

`PATCH /api/enclaves/{id}`

Perform a lifecycle action on an enclave.

| Body field | Required | Description                                 |
| ---------- | -------- | ------------------------------------------- |
| `action`   | Yes      | `pause`, `resume`, or `terminate`           |
| `account`  | Yes      | Account identifier (legacy `walletAddress`) |

```bash
curl -X PATCH https://app.trezalabs.com/api/enclaves/enc_123456 \
  -H "Authorization: Bearer <privy-access-token>" \
  -H "Content-Type: application/json" \
  -d '{ "action": "pause", "account": "user@company.com" }'
```

**Response** `200` — `{ "enclave": { ... }, "message": "Enclave pause initiated successfully" }`. `400` for an invalid action or enclave state, `403` if the account does not own the enclave.

### Delete a specific enclave

`DELETE /api/enclaves/{id}`

Delete a specific enclave by ID — only allowed when the enclave is in a terminal state.

| Query parameter | Required | Description                                                  |
| --------------- | -------- | ------------------------------------------------------------ |
| `account`       | Yes      | Account identifier for authorization (legacy alias `wallet`) |

```bash
curl -X DELETE "https://app.trezalabs.com/api/enclaves/enc_123456?account=user@company.com" \
  -H "Authorization: Bearer <privy-access-token>"
```

**Response** `200` — `{ "message": "Enclave deleted successfully" }`. `400` if the enclave is not in a terminal state, `403` on ownership mismatch.

### Get enclave logs

`GET /api/enclaves/{id}/logs`

Retrieve logs for an enclave from multiple sources.

| Query parameter | Required | Description                                                                            |
| --------------- | -------- | -------------------------------------------------------------------------------------- |
| `type`          | No       | `all` (default), `ecs`, `stepfunctions`, `lambda`, `application`, `errors`, or `build` |
| `limit`         | No       | Max log entries, 1–1000 (default 100)                                                  |

{% hint style="info" %}
`type=build` returns the CodeBuild Docker image build logs for enclaves built from GitHub; build logs are also included in `all` when the enclave has a build.
{% endhint %}

```bash
curl "https://app.trezalabs.com/api/enclaves/enc_123456/logs?type=application&limit=50" \
  -H "Authorization: Bearer <privy-access-token>"
```

**Response** `200`

```json
{
  "enclave_id": "enc_123456",
  "enclave_name": "Trading Bot Enclave",
  "enclave_status": "DEPLOYED",
  "logs": {
    "application": [
      {
        "timestamp": 1640995200000,
        "message": "Application started successfully",
        "source": "application",
        "stream": "ecs/treza-dev-terraform-runner/12345",
        "type": "stdout"
      }
    ]
  }
}
```

The `logs` object contains arrays per requested source (`ecs`, `stepfunctions`, `lambda`, `application`, `errors`, `build`). Log entries may also include `logGroup`, `function` (Lambda logs), `execution`, and `stateMachine` (`deployment` or `cleanup`) for Step Functions logs. Entry `type` is one of `application`, `stdout`, `stderr`, `info`, `error`.

### Get PCR measurements

`GET /api/enclaves/{id}/pcrs`

Returns Platform Configuration Register values extracted from the enclave's logs. PCR0 = enclave image hash, PCR1 = Linux kernel hash, PCR2 = application hash, PCR8 = signing certificate hash.

```bash
curl https://app.trezalabs.com/api/enclaves/enc_123456/pcrs \
  -H "Authorization: Bearer <privy-access-token>"
```

**Response** `200`

```json
{
  "pcrs": {
    "pcr0": "a1b2c3d4e5f67890abcdef...",
    "pcr1": "b2c3d4e5f67890abcdef12...",
    "pcr2": "c3d4e5f67890abcdef1234...",
    "pcr8": "d4e5f67890abcdef123456..."
  },
  "message": "PCR values retrieved from enclave logs"
}
```

`404` if the enclave is not found or no log streams are available yet.

### Get enclave attestation

`GET /api/enclaves/{id}/attestation`

Retrieve the cryptographic attestation document and verification details for a deployed enclave.

```bash
curl https://app.trezalabs.com/api/enclaves/enc_123456/attestation \
  -H "Authorization: Bearer <privy-access-token>"
```

**Response** `200`

```json
{
  "enclaveId": "enc_1234567890_abc123",
  "attestationDocument": {
    "moduleId": "nitro-enclave-enc_1234567890_abc123",
    "digest": "sha384:abc123def456...",
    "timestamp": 1702642200000,
    "pcrs": { "0": "a1b2...", "1": "b2c3...", "2": "c3d4...", "8": "d4e5..." },
    "certificate": "-----BEGIN CERTIFICATE-----\n...",
    "cabundle": ["-----BEGIN CERTIFICATE-----\n..."],
    "publicKey": "-----BEGIN PUBLIC KEY-----\n...",
    "userData": null,
    "nonce": "a1b2c3d4e5f6"
  },
  "verification": {
    "isValid": true,
    "trustLevel": "HIGH",
    "verificationStatus": "VERIFIED",
    "integrityScore": 98.5,
    "lastVerified": "2024-12-15T10:30:00Z",
    "errors": []
  },
  "endpoints": {
    "verificationUrl": "https://app.trezalabs.com/api/enclaves/enc_123456/attestation/verify",
    "apiEndpoint": "https://app.trezalabs.com/api/enclaves/enc_123456/attestation",
    "webhookUrl": "https://app.trezalabs.com/api/enclaves/enc_123456/attestation/webhook"
  }
}
```

`400` if the enclave is not deployed (response includes the current `status`), `404` if not found.

### Get verification status

`GET /api/enclaves/{id}/attestation/verify`

Quick verification status check for an enclave.

```bash
curl https://app.trezalabs.com/api/enclaves/enc_123456/attestation/verify \
  -H "Authorization: Bearer <privy-access-token>"
```

**Response** `200`

```json
{
  "enclaveId": "enc_1234567890_abc123",
  "isVerified": true,
  "status": "DEPLOYED",
  "lastVerified": "2024-12-15T10:30:00Z",
  "trustLevel": "HIGH"
}
```

`trustLevel` is one of `HIGH`, `MEDIUM`, `LOW`, `UNKNOWN`.

### Verify attestation document

`POST /api/enclaves/{id}/attestation/verify`

Perform comprehensive verification of an attestation document. The request body is optional.

| Body field            | Required | Description                                  |
| --------------------- | -------- | -------------------------------------------- |
| `attestationDocument` | No       | Base64-encoded attestation document          |
| `nonce`               | No       | Nonce for replay-attack protection           |
| `challenge`           | No       | Challenge string for additional verification |

```bash
curl -X POST https://app.trezalabs.com/api/enclaves/enc_123456/attestation/verify \
  -H "Authorization: Bearer <privy-access-token>" \
  -H "Content-Type: application/json" \
  -d '{ "nonce": "a1b2c3d4e5f6", "challenge": "verify_12345" }'
```

**Response** `200`

```json
{
  "isValid": true,
  "trustLevel": "HIGH",
  "verificationDetails": {
    "pcrVerification": true,
    "certificateChain": true,
    "timestampValid": true,
    "nonceMatches": false,
    "signatureValid": true
  },
  "complianceChecks": { "soc2": false, "hipaa": false, "fips": true, "commonCriteria": true },
  "riskScore": 5,
  "recommendations": ["Enclave attestation is valid and secure"],
  "verifiedAt": "2024-12-15T10:30:00Z"
}
```

`400` if the enclave is not deployed.

### Get ANS binding

`GET /api/enclaves/{id}/ans`

Return the stored Agent Name Service (ANS) binding for an enclave, the expected attestation `user_data` derived from it, and — with `sync=1` — the live registry record and sync status.

| Query parameter | Required | Description                                                           |
| --------------- | -------- | --------------------------------------------------------------------- |
| `sync`          | No       | Set to `1` to also fetch the ANS registry record and compute `inSync` |

```bash
curl "https://app.trezalabs.com/api/enclaves/enc_123456/ans?sync=1" \
  -H "Authorization: Bearer <privy-access-token>"
```

**Response** `200`

```json
{
  "enclaveId": "enc_123456",
  "stored": {
    "agentFqdn": "my-agent.ans.example",
    "certSpkiSha256": "…",
    "recordVersion": "1",
    "lastSyncedAt": "2026-06-01T00:00:00Z",
    "registryBaseUrl": "https://registry.example"
  },
  "expectedUserData": "…",
  "registry": { },
  "inSync": true,
  "syncError": null,
  "registryConfigured": true
}
```

`registry` and `inSync` are `null` unless `sync=1` is passed.

### Set ANS binding

`PATCH /api/enclaves/{id}/ans`

Store or update the ANS binding on an enclave's provider configuration.

| Body field        | Required | Description                                                       |
| ----------------- | -------- | ----------------------------------------------------------------- |
| `account`         | Yes      | Account identifier that owns the enclave (legacy `walletAddress`) |
| `agentFqdn`       | Yes      | Agent fully-qualified domain name in the ANS registry             |
| `certSpkiSha256`  | Yes      | SHA-256 of the agent certificate's SubjectPublicKeyInfo           |
| `recordVersion`   | Yes      | ANS record version the binding refers to                          |
| `registryBaseUrl` | No       | Per-binding registry base URL override                            |

```bash
curl -X PATCH https://app.trezalabs.com/api/enclaves/enc_123456/ans \
  -H "Authorization: Bearer <privy-access-token>" \
  -H "Content-Type: application/json" \
  -d '{
    "account": "user@company.com",
    "agentFqdn": "my-agent.ans.example",
    "certSpkiSha256": "<spki-sha256>",
    "recordVersion": "1"
  }'
```

**Response** `200` — `{ "enclaveId": "enc_123456", "ans": { ... }, "expectedUserData": "…" }`. `403` if the account does not own the enclave.

### Get ASE document

`GET /api/enclaves/{id}/ase`

Return the Attestation of Secure Execution (ASE) compliance document for a deployed enclave. The document is generated on first request and cached.

| Query parameter | Required | Description                                                          |
| --------------- | -------- | -------------------------------------------------------------------- |
| `refresh`       | No       | Set to `true` to regenerate instead of returning the cached document |

```bash
curl "https://app.trezalabs.com/api/enclaves/enc_123456/ase" \
  -H "Authorization: Bearer <privy-access-token>"
```

**Response** `200` — an ASE document containing:

* `metadata` — `documentId`, `documentType` (`ATTESTATION_OF_SECURE_EXECUTION`), `version`, `generatedAt`, `generatedBy`, `documentHash` (SHA-256 of the body)
* `enclave` — `id`, `name`, `description`, `provider`, `region`, `status`, `createdAt`
* `hardwareAttestation` — `platform`, `isolationLevel`, and `pcrs` (each PCR with `value` and `description`)
* `verification` — `status` (`VERIFIED`/`PENDING`), `trustLevel`, `integrityScore`, `details`, `verifiedAt`
* `compliance` — `soc2`, `hipaa`, `fips`, `commonCriteria` booleans
* `executionGuarantees` — array of guarantee statements

`400` if the enclave is not deployed.

### Validate a PII processor enclave

`POST /api/enclaves/{id}/pii/process`

Control-plane hook that validates an enclave is a `PII_PROCESSOR` and verifies its attestation before ciphertext is routed to it. Actual decryption and processing run inside the enclave.

| Body field            | Required | Description                 |
| --------------------- | -------- | --------------------------- |
| `attestationDocument` | No       | Base64 attestation document |

```bash
curl -X POST https://app.trezalabs.com/api/enclaves/enc_123456/pii/process \
  -H "Authorization: Bearer <privy-access-token>" \
  -H "Content-Type: application/json" \
  -d '{}'
```

**Response** `200` — `{ "enclaveId": "enc_123456", "ok": true, "attestationRef": "…" }`. `400` if the enclave is not a `PII_PROCESSOR`.

***

## Providers

### List providers

`GET /api/providers`

Retrieve all available enclave providers, or a specific provider by ID.

| Query parameter | Required | Description                                        |
| --------------- | -------- | -------------------------------------------------- |
| `id`            | No       | Specific provider ID to retrieve, e.g. `aws-nitro` |

```bash
curl https://app.trezalabs.com/api/providers
```

**Response** `200`

```json
{
  "providers": [
    {
      "id": "aws-nitro",
      "name": "AWS Nitro Enclaves",
      "description": "Secure isolated compute environments using AWS Nitro technology",
      "regions": ["us-east-1", "us-west-2", "eu-west-1"],
      "configSchema": {
        "dockerImage": { "type": "string", "label": "Docker Image", "required": true },
        "cpuCount": { "type": "number", "label": "CPU Count", "defaultValue": 2 }
      }
    }
  ]
}
```

When `id` is supplied the response is `{ "provider": { ... } }`; `404` if that provider does not exist.

***

## Docker

### Search Docker Hub or list repository tags

`GET /api/docker/search`

Search Docker Hub for images, or fetch the tags of a specific repository. Supply exactly one of `q` or `repo`.

| Query parameter | Required          | Description                                      |
| --------------- | ----------------- | ------------------------------------------------ |
| `q`             | One of `q`/`repo` | Search query for Docker images                   |
| `repo`          | One of `q`/`repo` | Repository to list tags for, e.g. `library/node` |

```bash
# Search images
curl "https://app.trezalabs.com/api/docker/search?q=hello-world"

# List repository tags
curl "https://app.trezalabs.com/api/docker/search?repo=library/node"
```

**Response** `200` — search results:

```json
{
  "count": 150,
  "results": [
    { "name": "hello-world", "description": "Hello World!", "stars": 1500, "official": true, "automated": false, "owner": "library" }
  ]
}
```

Or tags, when `repo` is used:

```json
{
  "tags": [
    { "name": "latest", "size": 13336, "lastUpdated": "2024-01-15T10:30:00Z", "digest": "sha256:abc123..." }
  ]
}
```

`400` when neither `q` nor `repo` is provided.

***

## GitHub Integration

### Get OAuth authorization URL

`GET /api/github/auth`

| Query parameter | Required | Description                                 |
| --------------- | -------- | ------------------------------------------- |
| `state`         | No       | Optional state parameter for the OAuth flow |

```bash
curl "https://app.trezalabs.com/api/github/auth?state=abc123"
```

**Response** `200` — `{ "authUrl": "https://github.com/login/oauth/authorize?client_id=...", "state": "abc123" }`.

### Exchange OAuth code for access token

`POST /api/github/auth`

| Body field | Required | Description                          |
| ---------- | -------- | ------------------------------------ |
| `code`     | Yes      | OAuth authorization code from GitHub |
| `state`    | No       | State parameter from the OAuth flow  |

```bash
curl -X POST https://app.trezalabs.com/api/github/auth \
  -H "Content-Type: application/json" \
  -d '{ "code": "abcdef123456", "state": "abc123" }'
```

**Response** `200`

```json
{
  "access_token": "gho_xxxxxxxxxxxxxxxxxxxx",
  "user": { "id": 12345, "login": "username", "name": "John Doe", "avatar_url": "https://avatars.githubusercontent.com/u/12345" }
}
```

`400` on an invalid authorization code.

### GitHub OAuth callback

`GET /api/github/callback`

Handles the GitHub OAuth redirect (`code`, `state`, `error` query parameters) and responds with a `302` redirect back to the platform with success or error parameters. Used by the OAuth flow — not called directly.

### List repositories

`GET /api/github/repositories`

| Query parameter | Required | Description         |
| --------------- | -------- | ------------------- |
| `token`         | Yes      | GitHub access token |

```bash
curl "https://app.trezalabs.com/api/github/repositories?token=gho_xxxxxxxxxxxxxxxxxxxx"
```

**Response** `200`

```json
{
  "repositories": [
    {
      "id": 123456,
      "name": "my-repo",
      "fullName": "username/my-repo",
      "description": "A sample repository",
      "private": false,
      "defaultBranch": "main",
      "language": "TypeScript",
      "updatedAt": "2024-01-15T00:00:00Z",
      "htmlUrl": "https://github.com/username/my-repo"
    }
  ]
}
```

`401` for an invalid access token.

### List repository branches

`POST /api/github/repositories`

| Body field    | Required | Description                           |
| ------------- | -------- | ------------------------------------- |
| `accessToken` | Yes      | GitHub access token                   |
| `repository`  | Yes      | Repository, e.g. `username/repo-name` |

```bash
curl -X POST https://app.trezalabs.com/api/github/repositories \
  -H "Content-Type: application/json" \
  -d '{ "accessToken": "gho_xxxxxxxxxxxxxxxxxxxx", "repository": "username/repo-name" }'
```

**Response** `200`

```json
{
  "branches": [
    { "name": "main", "commit": { "sha": "abc123def456", "url": "https://api.github.com/repos/username/repo/commits/abc123def456" } }
  ]
}
```

***

## Build

GitHub → Docker image build pipeline. Builds run on CodeBuild and push to the platform registry.

### Trigger a build

`POST /api/build`

| Body field     | Required | Description                                                                                          |
| -------------- | -------- | ---------------------------------------------------------------------------------------------------- |
| `enclaveId`    | Yes      | Enclave the image is built for                                                                       |
| `githubRepo`   | Yes      | Repository, e.g. `username/repo-name`                                                                |
| `githubBranch` | No       | Branch to build, e.g. `main`                                                                         |
| `githubToken`  | No       | GitHub access token for private repositories (stored temporarily; clean up with `DELETE /api/build`) |
| `account`      | Yes      | Account identifier that owns the enclave (legacy `walletAddress`)                                    |

```bash
curl -X POST https://app.trezalabs.com/api/build \
  -H "Authorization: Bearer <privy-access-token>" \
  -H "Content-Type: application/json" \
  -d '{
    "enclaveId": "enc_123456",
    "githubRepo": "username/repo-name",
    "githubBranch": "main",
    "account": "user@company.com"
  }'
```

**Response** `202`

```json
{
  "buildId": "…",
  "buildArn": "…",
  "imageUri": "…",
  "logGroupName": "…",
  "logStreamName": "…",
  "status": "IN_PROGRESS"
}
```

`imageUri` is the registry URI the built image will be pushed to.

### Poll build status

`GET /api/build`

| Query parameter | Required | Description                            |
| --------------- | -------- | -------------------------------------- |
| `buildId`       | Yes      | Build ID returned by `POST /api/build` |

```bash
curl "https://app.trezalabs.com/api/build?buildId=<buildId>" \
  -H "Authorization: Bearer <privy-access-token>"
```

**Response** `200` — `buildId`, `status` (CodeBuild status, e.g. `IN_PROGRESS`, `SUCCEEDED`, `FAILED`, `STOPPED`), `phases` (each with `type`, `status`, `durationSeconds`), `logGroupName`, `logStreamName`, `startTime`, `endTime`. `404` if the build is not found.

### Delete a build token secret

`DELETE /api/build`

Clean up the temporary GitHub token secret created for a build.

| Query parameter | Required | Description                                           |
| --------------- | -------- | ----------------------------------------------------- |
| `secretArn`     | Yes      | Secret identifier returned when the build was created |

```bash
curl -X DELETE "https://app.trezalabs.com/api/build?secretArn=<secret-id>" \
  -H "Authorization: Bearer <privy-access-token>"
```

**Response** `200` — `{ "message": "Secret deleted" }`.

***

## API Keys

### List API keys

`GET /api/api-keys`

Retrieve all API keys for the authenticated user. Auth: Privy bearer token (preferred); the `account` query parameter is a fallback identifier when no token is supplied.

| Query parameter | Required | Description                                         |
| --------------- | -------- | --------------------------------------------------- |
| `account`       | No       | Fallback account identifier (legacy alias `wallet`) |

```bash
curl https://app.trezalabs.com/api/api-keys \
  -H "Authorization: Bearer <privy-access-token>"
```

**Response** `200`

```json
{
  "apiKeys": [
    {
      "id": "key_123456",
      "name": "Production API Key",
      "keyHash": "sha256_hash_of_key",
      "permissions": ["enclaves:read", "tasks:read"],
      "status": "active",
      "account": "user@company.com",
      "createdAt": "2024-01-15T00:00:00Z",
      "updatedAt": "2024-01-15T00:00:00Z",
      "lastUsed": "2024-01-15T12:30:00Z"
    }
  ]
}
```

`401` when neither a bearer token nor an account identifier is supplied.

### Create API key

`POST /api/api-keys`

| Body field    | Required | Description                                                                                                                                                                                                               |
| ------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name`        | Yes      | Key name                                                                                                                                                                                                                  |
| `permissions` | Yes      | Array of scopes: `enclaves:read`, `enclaves:write`, `tasks:read`, `tasks:write`, `logs:read`, `pii:ingest`, `pii:write`, `pii:read`, `pii:delete`, `pii:consent`, `pii:audit`, `redact:run`, `redact:proxy`, `redact:log` |
| `account`     | Yes      | Account identifier (legacy `walletAddress`)                                                                                                                                                                               |

```bash
curl -X POST https://app.trezalabs.com/api/api-keys \
  -H "Authorization: Bearer <privy-access-token>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Production API Key",
    "permissions": ["redact:run", "redact:proxy", "redact:log"],
    "account": "user@company.com"
  }'
```

**Response** `201` — `{ "apiKey": { ... } }` including the `key` field.

{% hint style="warning" %}
The plaintext `key` (`treza_live_...`) is returned **only on creation** — store it securely. Subsequent reads return only the key's SHA-256 hash. Creating a key requires an active subscription or trial (`402` otherwise).
{% endhint %}

### Update API key

`PUT /api/api-keys`

| Body field    | Required | Description                                 |
| ------------- | -------- | ------------------------------------------- |
| `id`          | Yes      | API key ID                                  |
| `account`     | Yes      | Account identifier (legacy `walletAddress`) |
| `name`        | No       | New name                                    |
| `permissions` | No       | New permission scopes                       |
| `status`      | No       | `active` or `inactive`                      |

```bash
curl -X PUT https://app.trezalabs.com/api/api-keys \
  -H "Authorization: Bearer <privy-access-token>" \
  -H "Content-Type: application/json" \
  -d '{ "id": "key_123456", "account": "user@company.com", "status": "inactive" }'
```

**Response** `200` — `{ "apiKey": { ... } }`. `404` if not found or access denied.

### Delete API key

`DELETE /api/api-keys`

| Query parameter | Required | Description                                                  |
| --------------- | -------- | ------------------------------------------------------------ |
| `id`            | Yes      | API key ID to delete                                         |
| `account`       | Yes      | Account identifier for authorization (legacy alias `wallet`) |

```bash
curl -X DELETE "https://app.trezalabs.com/api/api-keys?id=key_123456&account=user@company.com" \
  -H "Authorization: Bearer <privy-access-token>"
```

**Response** `200` — `{ "message": "API key deleted successfully" }`.

***

## Tasks

Scheduled tasks that run within enclaves. Task `status` values: `running`, `stopped`, `failed`, `pending`.

### List tasks

`GET /api/tasks`

| Query parameter | Required | Description                                                |
| --------------- | -------- | ---------------------------------------------------------- |
| `account`       | Yes      | Account identifier to filter tasks (legacy alias `wallet`) |
| `enclave`       | No       | Enclave ID to filter tasks                                 |

```bash
curl "https://app.trezalabs.com/api/tasks?account=user@company.com&enclave=enc_123456" \
  -H "Authorization: Bearer <privy-access-token>"
```

**Response** `200`

```json
{
  "tasks": [
    {
      "id": "task_123456",
      "name": "Daily Price Monitor",
      "description": "Monitor cryptocurrency prices and send alerts",
      "enclaveId": "enc_123456",
      "status": "running",
      "schedule": "0 9 * * *",
      "account": "user@company.com",
      "createdAt": "2024-01-15T00:00:00Z",
      "updatedAt": "2024-01-15T00:00:00Z",
      "lastRun": "2024-01-15T09:00:00Z"
    }
  ]
}
```

### Create task

`POST /api/tasks`

| Body field    | Required | Description                                      |
| ------------- | -------- | ------------------------------------------------ |
| `name`        | Yes      | Task name                                        |
| `description` | Yes      | Description                                      |
| `enclaveId`   | Yes      | Enclave the task runs in                         |
| `schedule`    | Yes      | Cron-style schedule expression, e.g. `0 9 * * *` |
| `account`     | Yes      | Account identifier (legacy `walletAddress`)      |

```bash
curl -X POST https://app.trezalabs.com/api/tasks \
  -H "Authorization: Bearer <privy-access-token>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Daily Price Monitor",
    "description": "Monitor cryptocurrency prices and send alerts",
    "enclaveId": "enc_123456",
    "schedule": "0 9 * * *",
    "account": "user@company.com"
  }'
```

**Response** `201` — `{ "task": { ... } }`.

### Update task

`PUT /api/tasks`

| Body field                        | Required | Description                                  |
| --------------------------------- | -------- | -------------------------------------------- |
| `id`                              | Yes      | Task ID                                      |
| `account`                         | Yes      | Account identifier (legacy `walletAddress`)  |
| `name`, `description`, `schedule` | No       | Fields to update                             |
| `status`                          | No       | `running`, `stopped`, `failed`, or `pending` |

```bash
curl -X PUT https://app.trezalabs.com/api/tasks \
  -H "Authorization: Bearer <privy-access-token>" \
  -H "Content-Type: application/json" \
  -d '{ "id": "task_123456", "account": "user@company.com", "schedule": "0 */6 * * *" }'
```

**Response** `200` — `{ "task": { ... } }`. `404` if not found or access denied.

### Delete task

`DELETE /api/tasks`

| Query parameter | Required | Description                                                  |
| --------------- | -------- | ------------------------------------------------------------ |
| `id`            | Yes      | Task ID to delete                                            |
| `account`       | Yes      | Account identifier for authorization (legacy alias `wallet`) |

```bash
curl -X DELETE "https://app.trezalabs.com/api/tasks?id=task_123456&account=user@company.com" \
  -H "Authorization: Bearer <privy-access-token>"
```

**Response** `200` — `{ "message": "Task deleted successfully" }`.

***

## KYC

Zero-knowledge proof KYC verification. Proofs expire automatically 7 days after verification.

### Submit ZK proof for KYC verification

`POST /api/kyc/proof`

Submit a zero-knowledge proof for KYC verification. The proof is validated, stored by the platform, and optionally submitted to the blockchain for immutable verification. Open endpoint (no authentication); cryptographic proof validation prevents fake submissions. Recommended rate limit: 10 requests/hour per IP.

| Body field   | Required | Description                                |
| ------------ | -------- | ------------------------------------------ |
| `userId`     | Yes      | User identifier                            |
| `proof`      | Yes      | ZK proof object (see below)                |
| `deviceInfo` | No       | `{ "platform": "iOS", "version": "17.0" }` |

The `proof` object:

| Field          | Required | Description                                        |
| -------------- | -------- | -------------------------------------------------- |
| `commitment`   | Yes      | 64-character hex commitment hash                   |
| `proof`        | Yes      | Zero-knowledge proof data (minimum 65 characters)  |
| `publicInputs` | Yes      | Array of public inputs (at least 1)                |
| `timestamp`    | Yes      | Proof generation timestamp — must be within 1 hour |
| `algorithm`    | Yes      | `Pedersen-SHA256`                                  |

```bash
curl -X POST https://app.trezalabs.com/api/kyc/proof \
  -H "Content-Type: application/json" \
  -d '{
    "userId": "user_abc123",
    "proof": {
      "commitment": "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
      "proof": "0x1234567890abcdef...proof_data_here...987654321fedcba",
      "publicInputs": ["0x1234567890abcdef", "0xfedcba0987654321"],
      "timestamp": "2024-12-15T10:30:00Z",
      "algorithm": "Pedersen-SHA256"
    }
  }'
```

**Response** `201`

```json
{
  "proofId": "550e8400-e29b-41d4-a716-446655440000",
  "blockchainProofId": "0x1234567890abcdef",
  "verificationUrl": "/api/kyc/proof/550e8400-e29b-41d4-a716-446655440000",
  "expiresAt": "2024-12-22T10:30:00Z",
  "chainTxHash": "0xabcdef..."
}
```

`blockchainProofId` and `chainTxHash` are present only if blockchain submission succeeded. `400` for missing fields, an invalid proof format, or failed proof verification.

### Get proof details

`GET /api/kyc/proof/{proofId}`

Retrieve details for a KYC proof. By default returns public data only.

| Query parameter  | Required | Description                                                                                      |
| ---------------- | -------- | ------------------------------------------------------------------------------------------------ |
| `includePrivate` | No       | Include private proof data (`proof` signature and original `timestamp`); requires authentication |

```bash
curl https://app.trezalabs.com/api/kyc/proof/550e8400-e29b-41d4-a716-446655440000
```

**Response** `200`

```json
{
  "proofId": "550e8400-e29b-41d4-a716-446655440000",
  "commitment": "1234567890abcdef...",
  "publicInputs": ["0x1234567890abcdef"],
  "algorithm": "Pedersen-SHA256",
  "verifiedAt": "2024-12-15T10:30:00Z",
  "expiresAt": "2024-12-22T10:30:00Z"
}
```

### Verify a proof by ID

`GET /api/kyc/proof/{proofId}/verify`

Verify the validity of a KYC proof, checking expiration and blockchain status.

```bash
curl https://app.trezalabs.com/api/kyc/proof/550e8400-e29b-41d4-a716-446655440000/verify
```

**Response** `200`

```json
{
  "proofId": "550e8400-e29b-41d4-a716-446655440000",
  "isValid": true,
  "publicInputs": ["0x1234567890abcdef"],
  "verifiedAt": "2024-12-15T10:30:00Z",
  "chainVerified": true,
  "expiresAt": "2024-12-22T10:30:00Z"
}
```

`410` when the proof has expired (response includes `expiresAt`), `404` if not found.

### Bridge a verified KYC proof to encrypted PII

`POST /api/kyc/pii-bridge`

Validates a verified KYC proof (status, commitment, and ownership), then encrypts and stores the associated PII payload, binding the record to the proof's commitment via `piiArtifactHash`.

**Auth:** Bearer API key with `pii:ingest` or `pii:write` plus the `x-treza-account` header (must match the proof's `userId`), or a Privy access token.

| Body field           | Required | Description                                                                |
| -------------------- | -------- | -------------------------------------------------------------------------- |
| `proofId`            | Yes      | Verified KYC proof ID (UUID)                                               |
| `commitment`         | Yes      | 32-byte hex commitment (with or without `0x`); must match the proof record |
| `type`               | Yes      | `SSN`, `PASSPORT`, `DRIVERS_LICENSE`, or `MEDICAL_RECORD`                  |
| `payload`            | Yes      | The PII payload to encrypt (any JSON value)                                |
| `consentGiven`       | Yes      | Must be `true`                                                             |
| `processorEnclaveId` | No       | Optional `PII_PROCESSOR` enclave to bind the record to                     |

```bash
curl -X POST https://app.trezalabs.com/api/kyc/pii-bridge \
  -H "Authorization: Bearer treza_live_..." \
  -H "x-treza-account: user@company.com" \
  -H "Content-Type: application/json" \
  -d '{
    "proofId": "550e8400-e29b-41d4-a716-446655440000",
    "commitment": "0x1234...",
    "type": "PASSPORT",
    "payload": { "passportNumber": "X1234567" },
    "consentGiven": true
  }'
```

**Response** `200` — `{ "piiId": "…", "proofId": "…", "piiArtifactHash": "…", "createdAt": "…", "onChainHint": "…" }`. `piiArtifactHash` is a `bytes32` hash binding the PII artifact to the KYC commitment; `onChainHint` carries optional instructions for binding the artifact hash on-chain via KYCVerifier. `403` if the proof is not verified or not owned by the calling account, `410` if the proof has expired.

***

## ZKVerify

Treza acts as a managed gateway to the zkVerify network via the Horizen Relayer — you never need Relayer credentials. Supported proof types: `groth16` (default), `risc0`, `ultrahonk`, `ultraplonk`, `sp1`, `ezkl`. See the [zkVerify guide](/developers/zkverify.md) for the full walkthrough and on-chain integration.

### Register a verification key

`POST /api/zkverify/register-vk`

Register a verification key once per circuit; reuse the returned `vkHash` for every proof submission.

| Body field     | Required | Description                                                                                                 |
| -------------- | -------- | ----------------------------------------------------------------------------------------------------------- |
| `vk`           | Yes      | Verification key data structure (format depends on the proof type)                                          |
| `proofType`    | No       | One of the supported proof types. Default: `groth16`                                                        |
| `proofOptions` | No       | `library` (e.g. `snarkjs`, `gnark`), `curve` (e.g. `bn128`, `bls12_381`), `numberOfPublicInputs`, `version` |

```bash
curl -X POST https://app.trezalabs.com/api/zkverify/register-vk \
  -H "Content-Type: application/json" \
  -d '{
    "vk": { "protocol": "groth16", "curve": "bn128", "nPublic": 2, "...": "..." },
    "proofType": "groth16",
    "proofOptions": { "library": "snarkjs", "curve": "bn128" }
  }'
```

**Response** `200`

```json
{
  "success": true,
  "vkHash": "0x...",
  "meta": { },
  "message": "Verification key registered successfully"
}
```

{% hint style="info" %}
If the key was already registered, the endpoint still returns `success: true` with the existing `vkHash` and `"alreadyRegistered": true` — registration is idempotent from your perspective. Store the `vkHash`; you'll pass it with every proof.
{% endhint %}

### Check verification key registration

`GET /api/zkverify/register-vk`

| Query parameter | Required | Description                           |
| --------------- | -------- | ------------------------------------- |
| `vkHash`        | Yes      | Hash of the verification key to check |

```bash
curl "https://app.trezalabs.com/api/zkverify/register-vk?vkHash=0x..."
```

**Response** `200` — `{ "success": true, "isRegistered": false }`.

{% hint style="warning" %}
This check is currently a stub that always reports `isRegistered: false`. Rely on the idempotent `POST` (`alreadyRegistered: true`) instead.
{% endhint %}

### Submit a ZK proof for verification

`POST /api/zkverify/submit-proof`

| Body field      | Required | Description                                                              |
| --------------- | -------- | ------------------------------------------------------------------------ |
| `proof`         | Yes      | The ZK proof data                                                        |
| `publicSignals` | Yes      | Array of public signals / inputs                                         |
| `vk`            | Yes      | The `vkHash` returned by register-vk (keys are submitted as registered)  |
| `proofType`     | No       | Default: `groth16`                                                       |
| `chainId`       | No       | Target EVM chain for aggregation. Default: `11155111` (Ethereum Sepolia) |
| `userAddress`   | No       | Optional wallet address of the submitter                                 |

```bash
curl -X POST https://app.trezalabs.com/api/zkverify/submit-proof \
  -H "Content-Type: application/json" \
  -d '{
    "proof": "0x...",
    "publicSignals": ["0x...", "0x..."],
    "vk": "0x<vkHash from register-vk>",
    "proofType": "groth16",
    "chainId": 11155111
  }'
```

**Response** `200`

```json
{
  "success": true,
  "jobId": "job_abc123",
  "optimisticVerify": "success",
  "message": "Proof submitted for verification"
}
```

`400` when fields are missing or the Relayer's optimistic verification fails — in that case the error response includes the `jobId` and relayer `details`.

### Get verification job status

`GET /api/zkverify/job-status/{jobId}`

Check the status of a submitted verification job.

```bash
curl https://app.trezalabs.com/api/zkverify/job-status/job_abc123
```

**Response** `200`

```json
{
  "success": true,
  "jobStatus": {
    "jobId": "job_abc123",
    "status": "Finalized",
    "statusId": 5,
    "proofType": "groth16",
    "chainId": 11155111,
    "createdAt": "2026-06-01T10:00:00Z",
    "updatedAt": "2026-06-01T10:02:00Z",
    "txHash": "0x...",
    "blockHash": "0x...",
    "aggregationId": "agg_xyz789",
    "statement": "0x...",
    "aggregationDetails": { }
  }
}
```

`status` is one of the relayer job statuses: `Queued`, `Valid`, `Submitted`, `IncludedInBlock`, `Finalized`, `Failed`, `AggregationPending`, `Aggregated`, `AggregationPublished`. `aggregationId`, `statement`, and `aggregationDetails` appear once the job is aggregated.

{% hint style="info" %}
Typical progression: `Queued` → `Valid` → `Submitted` → `IncludedInBlock` → `Finalized` → `Aggregated` → `AggregationPublished`. Poll until you reach `Aggregated` (or `AggregationPublished`) before fetching aggregation data for on-chain verification.
{% endhint %}

### Get aggregation details

`GET /api/zkverify/aggregation/{aggregationId}`

Retrieve the data needed to verify proofs on-chain using zkVerify's `IVerifyProofAggregation` contract.

```bash
curl https://app.trezalabs.com/api/zkverify/aggregation/agg_xyz789
```

**Response** `200`

```json
{
  "success": true,
  "aggregation": {
    "aggregationId": "agg_xyz789",
    "domainId": 0,
    "root": "0x...",
    "leafCount": 16,
    "leafIndex": 3,
    "merkleProof": ["0x...", "0x..."],
    "leaf": "0x...",
    "statement": "0x...",
    "receipt": "0x...",
    "receiptBlockHash": "0x..."
  }
}
```

`merkleProof` is the Merkle proof path your contract passes to `verifyProofAggregation()`.

### Get aggregation data by job ID

`GET /api/zkverify/aggregation/job/{jobId}`

Convenience endpoint: fetches job status and, if the job is aggregated, returns the aggregation details in one call.

```bash
curl https://app.trezalabs.com/api/zkverify/aggregation/job/job_abc123
```

**Response** `200` — when aggregated, the same `aggregation` object as above (plus `jobId`). When not yet aggregated:

```json
{
  "success": false,
  "message": "Job is not yet aggregated. Current status: Finalized",
  "jobStatus": { "jobId": "job_abc123", "status": "Finalized", "statusId": 5 }
}
```

{% hint style="warning" %}
The legacy `POST /api/zkverify/submit-attestation` oracle endpoint is **deprecated** and intentionally not documented here — use the job-status and aggregation endpoints above.
{% endhint %}

***

## ZKPassport

TEE-attested identity verification with ZKPassport. See the [ZKPassport guide](/developers/zkpassport.md) for the full client-side flow.

### Register a verification request

`POST /api/zkpassport/request`

Register a ZKPassport verification request server-side for tamper prevention. The client first calls `zkPassport.request()` in the browser to obtain the bridge `requestId`, then registers the declared claims here; the server builds and owns the query that `verify` checks against. Requests expire 30 minutes after registration.

| Body field        | Required | Description                                                         |
| ----------------- | -------- | ------------------------------------------------------------------- |
| `requestId`       | Yes      | Bridge topic returned by `zkPassport.request()` on the client       |
| `claims`          | Yes      | Array of claim constraints (at least 1, see below)                  |
| `scope`           | No       | Scope for the pseudonymous unique identifier, e.g. `kyc-onboarding` |
| `validitySeconds` | No       | ID freshness window passed to the SDK (default `604800` = 7 days)   |
| `devMode`         | No       | Default `false`                                                     |

Each claim has an `op` and op-specific fields: `disclose` (`field`), comparisons `gte`/`gt`/`lte`/`lt`/`eq` (`field`, `value`), set ops `in`/`out` (`field`, `values`), `range` (`field`, `min`, `max`), `facematch` (`mode`: `regular`/`strict`), `sanctions` (`countries`, `lists`, `strict`).

```bash
curl -X POST https://app.trezalabs.com/api/zkpassport/request \
  -H "Content-Type: application/json" \
  -d '{
    "requestId": "<bridge-request-id>",
    "claims": [
      { "op": "gte", "field": "age", "value": 18 },
      { "op": "disclose", "field": "nationality" }
    ],
    "scope": "kyc-onboarding"
  }'
```

**Response** `201` — `{ "requestId": "…", "expiresAt": "…" }`. `409` if a request with this `requestId` is already registered.

### Verify ZKPassport proofs

`POST /api/zkpassport/verify`

Verify the proofs and `queryResult` generated by the ZKPassport mobile app against the server-stored original query. In production, verification runs inside an AWS Nitro Enclave.

| Body field    | Required | Description                                                                                           |
| ------------- | -------- | ----------------------------------------------------------------------------------------------------- |
| `requestId`   | Yes      | From `POST /api/zkpassport/request`                                                                   |
| `proofs`      | Yes      | Proofs collected via `onProofGenerated()` callbacks (`proof`, `vkeyHash`, `version`, `name`, `total`) |
| `queryResult` | Yes      | `QueryResult` from the `onResult()` callback (claimed result; verified server-side)                   |

```bash
curl -X POST https://app.trezalabs.com/api/zkpassport/verify \
  -H "Content-Type: application/json" \
  -d '{ "requestId": "<requestId>", "proofs": [ { "proof": "...", "vkeyHash": "..." } ], "queryResult": { } }'
```

**Response** `200`

```json
{
  "attestation": {
    "attestationId": "…",
    "verifiedAt": "2026-06-01T10:00:00Z",
    "verified": true,
    "uniqueIdentifier": "…",
    "claims": [ { "field": "age", "result": true, "expected": ">= 18" } ],
    "tee": { "provider": "aws-nitro", "pcr0": "…", "domain": "…" }
  },
  "nitroUserDataBase64": "…"
}
```

{% hint style="warning" %}
An attestation with `verified: false` is still returned with HTTP `200` — always check `attestation.verified`. Transport errors use 4xx/5xx: `404` unknown request, `409` already verified, `410` expired. For hardware-rooted trust, verify `nitroUserDataBase64` (COSE\_Sign1 Nitro attestation user data) against the AWS Nitro root CA — the bare `tee.pcr*` fields are advisory.
{% endhint %}

### Poll request status

`GET /api/zkpassport/verify`

| Query parameter | Required | Description         |
| --------------- | -------- | ------------------- |
| `requestId`     | Yes      | Request ID to check |

```bash
curl "https://app.trezalabs.com/api/zkpassport/verify?requestId=<requestId>"
```

**Response** `200` — `requestId`, `status` (`pending`, `verified`, `failed`, `expired`), `createdAt`, `updatedAt`, `expiresAt`, and — if a verification was attempted — `lastAttestationId` and `lastAttestationVerified`.

***

## PII

Encrypted PII vault with KMS envelope encryption at rest. Plaintext is never returned by the API — envelopes must be decrypted inside an approved TEE.

**Auth for all PII endpoints:** `Authorization: Bearer treza_live_...` API key with the relevant `pii:*` scope, plus the `x-treza-account` header (legacy `x-treza-wallet`) matching the key owner. A Privy access token may be used instead (full PII permissions over the caller's own data).

### Ingest encrypted PII

`POST /api/pii/ingest` — requires `pii:ingest` or `pii:write`

| Body field           | Required | Description                                            |
| -------------------- | -------- | ------------------------------------------------------ |
| `type`               | Yes      | Data type label, e.g. `SSN`                            |
| `payload`            | Yes      | The PII payload to encrypt (any JSON value)            |
| `consentGiven`       | No       | Default `false`                                        |
| `processorEnclaveId` | No       | Optional `PII_PROCESSOR` enclave to bind the record to |
| `metadata`           | No       | Arbitrary metadata stored alongside the record         |

```bash
curl -X POST https://app.trezalabs.com/api/pii/ingest \
  -H "Authorization: Bearer treza_live_..." \
  -H "x-treza-account: user@company.com" \
  -H "Content-Type: application/json" \
  -d '{ "type": "SSN", "payload": { "ssn": "123-45-6789" }, "consentGiven": true }'
```

**Response** `200` — `{ "piiId": "pii_1234567890_abc123", "processorEnclaveId": null, "encryption": "AES_256_GCM_KMS_ENVELOPE" }`.

### List PII record metadata

`GET /api/pii/list` — requires `pii:read`

Lists record metadata only — never ciphertext.

| Query parameter | Required | Description                                                          |
| --------------- | -------- | -------------------------------------------------------------------- |
| `account`       | Yes      | Account identifier (legacy alias `wallet`); must match the key owner |

```bash
curl "https://app.trezalabs.com/api/pii/list?account=user@company.com" \
  -H "Authorization: Bearer treza_live_..."
```

**Response** `200`

```json
{
  "records": [
    { "piiId": "pii_1234567890_abc123", "dataType": "SSN", "status": "active", "createdAt": "2026-06-01T00:00:00Z", "processorEnclaveId": null }
  ]
}
```

`status` is `active` or `pending_delete`.

### Retrieve an encrypted PII envelope

`POST /api/pii/retrieve` — requires `pii:read`

Returns the encrypted envelope for a record. Requires an active consent matching the record's data type and the requested purpose. When `processorEnclaveId` is supplied, the target enclave's attestation is verified before release.

| Body field            | Required | Description                                                                                        |
| --------------------- | -------- | -------------------------------------------------------------------------------------------------- |
| `piiId`               | Yes      | Record ID                                                                                          |
| `purpose`             | Yes      | Purpose of access — must match an active consent recipient (or a consent with recipient `any`/`*`) |
| `processorEnclaveId`  | No       | `PII_PROCESSOR` enclave that will process the data; attestation verified before release            |
| `attestationDocument` | No       | Base64 attestation document for the processor enclave                                              |

```bash
curl -X POST https://app.trezalabs.com/api/pii/retrieve \
  -H "Authorization: Bearer treza_live_..." \
  -H "x-treza-account: user@company.com" \
  -H "Content-Type: application/json" \
  -d '{ "piiId": "pii_1234567890_abc123", "purpose": "kyc-screening" }'
```

**Response** `200`

```json
{
  "piiId": "pii_1234567890_abc123",
  "dataType": "SSN",
  "purpose": "kyc-screening",
  "envelope": {
    "kmsKeyId": "…",
    "encryptedDataKey": "<base64>",
    "iv": "<base64>",
    "authTag": "<base64>",
    "ciphertext": "<base64>",
    "algorithm": "AES_256_GCM_KMS_ENVELOPE"
  },
  "processorEnclaveId": null,
  "note": "Decrypt envelope inside an approved TEE using KMS."
}
```

`403` when the record belongs to another account, no active consent matches, or attestation verification failed; `404` when the record is not found or not active.

### Delete a PII record

`POST /api/pii/delete` — requires `pii:delete`

Soft-delete: the ciphertext envelope is removed immediately, the record is marked `pending_delete`, and the tombstone row expires after 30 days for compliance retention.

| Body field | Required | Description |
| ---------- | -------- | ----------- |
| `piiId`    | Yes      | Record ID   |

```bash
curl -X POST https://app.trezalabs.com/api/pii/delete \
  -H "Authorization: Bearer treza_live_..." \
  -H "x-treza-account: user@company.com" \
  -H "Content-Type: application/json" \
  -d '{ "piiId": "pii_1234567890_abc123" }'
```

**Response** `200` — `{ "piiId": "…", "status": "pending_delete", "ttl": 1750000000 }` (`ttl` is the Unix timestamp when the tombstone expires). `409` if the record is not active (already deleted).

### List PII consents

`GET /api/pii/consent` — requires `pii:consent`

| Query parameter | Required | Description                                                          |
| --------------- | -------- | -------------------------------------------------------------------- |
| `account`       | Yes      | Account identifier (legacy alias `wallet`); must match the key owner |

```bash
curl "https://app.trezalabs.com/api/pii/consent?account=user@company.com" \
  -H "Authorization: Bearer treza_live_..."
```

**Response** `200`

```json
{
  "consents": [
    { "consentId": "consent_1234567890_abc123", "dataType": "SSN", "recipient": "kyc-screening", "active": true, "createdAt": "2026-06-01T00:00:00Z" }
  ]
}
```

### Grant or revoke a PII consent

`POST /api/pii/consent` — requires `pii:consent`

Grant a consent (`dataType` + `recipient`) or revoke an existing one (`revoke: true` + `consentId`). Granted consents expire automatically after 3 years.

| Body field  | Required   | Description                                                                  |
| ----------- | ---------- | ---------------------------------------------------------------------------- |
| `dataType`  | For grant  | Data type, e.g. `SSN`                                                        |
| `recipient` | For grant  | Purpose/recipient the consent applies to (`any` or `*` matches all purposes) |
| `revoke`    | For revoke | Set `true` (with `consentId`) to revoke                                      |
| `consentId` | For revoke | Consent to revoke                                                            |

```bash
# Grant
curl -X POST https://app.trezalabs.com/api/pii/consent \
  -H "Authorization: Bearer treza_live_..." \
  -H "x-treza-account: user@company.com" \
  -H "Content-Type: application/json" \
  -d '{ "dataType": "SSN", "recipient": "kyc-screening" }'

# Revoke
curl -X POST https://app.trezalabs.com/api/pii/consent \
  -H "Authorization: Bearer treza_live_..." \
  -H "x-treza-account: user@company.com" \
  -H "Content-Type: application/json" \
  -d '{ "revoke": true, "consentId": "consent_1234567890_abc123" }'
```

**Response** `200` — grant: `{ "consentId": "…", "dataType": "SSN", "recipient": "kyc-screening", "active": true }`; revoke: `{ "consentId": "…", "active": false }`. `404` when revoking a consent not owned by the account.

### Query PII access audit events

`GET /api/pii/audit` — requires `pii:audit`

Query the PII access audit log. Either `workflowId` or `account` is required. Account-scoped queries must match the authenticated account; `workflowId`-only queries return only events belonging to the authenticated account.

| Query parameter | Required                      | Description                                                   |
| --------------- | ----------------------------- | ------------------------------------------------------------- |
| `workflowId`    | One of `workflowId`/`account` | Filter by workflow ID                                         |
| `workflowRunId` | No                            | Filter by a specific workflow run                             |
| `account`       | One of `workflowId`/`account` | Account identifier to scope the query (legacy alias `wallet`) |
| `violations`    | No                            | Set `true` to return only events with violations              |
| `startDate`     | No                            | Only return events at or after this timestamp                 |
| `limit`         | No                            | Max events (default 100, max 500)                             |

```bash
curl "https://app.trezalabs.com/api/pii/audit?account=user@company.com&violations=true" \
  -H "Authorization: Bearer treza_live_..." \
  -H "x-treza-account: user@company.com"
```

**Response** `200`

```json
{
  "summary": { "total": 42, "violations": 1, "workflows": ["wf_abc"] },
  "events": [
    {
      "eventId": "…",
      "workflowId": "wf_abc",
      "workflowRunId": "run_1",
      "stepId": "step_2",
      "stepName": "screening",
      "piiFieldsPresent": ["ssn"],
      "piiFieldsAllowed": ["ssn"],
      "piiFieldsStripped": [],
      "hadViolation": false,
      "timestamp": "2026-06-01T00:00:00Z",
      "account": "user@company.com"
    }
  ]
}
```

### Ingest PII access audit events

`POST /api/pii/audit` — requires `pii:audit`

Batch-ingest PII access events flushed from the Treza SDK. Events missing `workflowId`, `workflowRunId`, or `stepId` are skipped. Events are retained for 90 days. Stored events are attributed to the authenticated account; any `account`/`walletAddress` fields in the request body are ignored.

| Body field | Required | Description                                 |
| ---------- | -------- | ------------------------------------------- |
| `events`   | Yes      | Array of PII access events (shape as above) |

```bash
curl -X POST https://app.trezalabs.com/api/pii/audit \
  -H "Authorization: Bearer treza_live_..." \
  -H "x-treza-account: user@company.com" \
  -H "Content-Type: application/json" \
  -d '{ "events": [ { "workflowId": "wf_abc", "workflowRunId": "run_1", "stepId": "step_2", "hadViolation": false, "timestamp": "2026-06-01T00:00:00Z" } ] }'
```

**Response** `201` — `{ "stored": 1 }`.

***

## Redaction Proxy

PII redaction pipeline. Enterprise plans run redaction inside a TEE (mode `tee`); other plans use managed software redaction (mode `standard`).

### Redact PII from text

`POST /api/redact/run` — requires API key with `redact:run`

| Body field | Required | Description    |
| ---------- | -------- | -------------- |
| `text`     | Yes      | Text to redact |

```bash
curl -X POST https://app.trezalabs.com/api/redact/run \
  -H "Authorization: Bearer treza_live_..." \
  -H "Content-Type: application/json" \
  -d '{ "text": "Email john@example.com about the meeting" }'
```

**Response** `200`

```json
{
  "redacted": "Email [EMAIL_1] about the meeting",
  "entities": [
    { "type": "EMAIL", "placeholder": "[EMAIL_1]", "start": 6, "end": 15 }
  ],
  "requestId": "…",
  "mode": "standard",
  "modelVersion": "…",
  "recognizerVersion": "…"
}
```

On TEE-enabled plans the response also includes `attestation` (`enclaveId`, `region`, `attested`). `502` if redaction failed — the original text was not stored or forwarded.

### OpenAI-compatible chat completions with PII redaction

`POST /api/redact/chat/completions` — requires API key with `redact:proxy`

Drop-in replacement for an OpenAI-style `/v1/chat/completions` endpoint. Message content is passed through the redaction pipeline before being forwarded to the upstream provider; the upstream response is returned unchanged (the status code mirrors the upstream). Both buffered and streaming responses are supported: with `stream: true`, the upstream Server-Sent Events stream is piped straight through. The proxy never rewrites the response body — when rehydration is requested, the placeholder→original map is returned in the `x-treza-rehydration` header (for streams it is sent before the first token, so rehydrate client-side against accumulated text since a placeholder may span SSE chunks).

| Header              | Required | Description                                                                                                 |
| ------------------- | -------- | ----------------------------------------------------------------------------------------------------------- |
| `x-model-key`       | No       | Upstream provider API key (passthrough). Ignored when the selected proxy has a stored upstream key          |
| `x-treza-proxy`     | No       | ID of a configured redaction proxy to use (upstream URL, stored key, and policy)                            |
| `x-treza-rehydrate` | No       | Set to `1` to receive the placeholder→original rehydration map in the `x-treza-rehydration` response header |

| Body field | Required | Description                                                                                                              |
| ---------- | -------- | ------------------------------------------------------------------------------------------------------------------------ |
| `messages` | Yes      | OpenAI-style chat messages; string content and text parts are redacted before forwarding                                 |
| `model`    | No       | Forwarded to the upstream provider                                                                                       |
| `stream`   | No       | Set `true` for a Server-Sent Events stream piped through from the upstream; omit or `false` for a buffered JSON response |

Additional body properties are forwarded to the upstream as-is.

```bash
curl -X POST https://app.trezalabs.com/api/redact/chat/completions \
  -H "Authorization: Bearer treza_live_..." \
  -H "x-treza-proxy: proxy_1234567890_abc123" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-4o-mini",
    "messages": [ { "role": "user", "content": "Draft a reply to john@example.com" } ]
  }'
```

**Response** `200` — the upstream provider's JSON (e.g. an OpenAI chat completion object), plus Treza response headers:

| Response header       | Description                                                                |
| --------------------- | -------------------------------------------------------------------------- |
| `x-treza-request-id`  | Redaction request ID (audit log reference)                                 |
| `x-treza-mode`        | Redaction mode used (`tee` or `standard`)                                  |
| `x-treza-rehydration` | JSON map of placeholder→original values (only when `x-treza-rehydrate: 1`) |

Errors: `400` invalid JSON / missing messages / no upstream key available; `402` `PLAN_LIMIT` when the plan's request limit is reached; `403` missing `redact:proxy` permission or the selected proxy is paused; `404` unknown proxy; `502` redaction failed (messages were **not** forwarded), stored upstream key could not be loaded, or the upstream fetch failed.

### Get redaction audit log

`GET /api/redact/log` — requires API key with `redact:log`

Lists redaction audit entries for the calling API key (newest first). Entries contain entity counts by type — never the original or redacted text.

| Query parameter | Required | Description                                        |
| --------------- | -------- | -------------------------------------------------- |
| `limit`         | No       | Max entries, 1–500 (default 20)                    |
| `since`         | No       | Only return entries at or after this ISO timestamp |

```bash
curl "https://app.trezalabs.com/api/redact/log?limit=50" \
  -H "Authorization: Bearer treza_live_..."
```

**Response** `200`

```json
{
  "entries": [
    {
      "ts": "2026-06-01T10:00:00Z",
      "requestId": "…",
      "source": "proxy",
      "entityCountsByType": { "EMAIL": 2, "PHONE": 1 },
      "attestationRef": "enc_123456",
      "modelVersion": "…",
      "recognizerVersion": "…"
    }
  ]
}
```

`source` is `run` or `proxy`; `attestationRef` is the enclave ID the redaction ran on.

### Get redaction enclave attestation

`GET /api/redact/attest` — requires API key with `redact:run`

Fetch the attestation summary for the TEE redaction enclave. Requires an Enterprise plan (TEE feature); other plans receive `403` with code `PLAN_FEATURE`.

```bash
curl https://app.trezalabs.com/api/redact/attest \
  -H "Authorization: Bearer treza_live_..."
```

**Response** `200` — `{ "enclaveId": "…", "region": "…", "modelVersion": "…", "recognizerVersion": "…", "attested": true }`. `attested` is `true` only when redaction runs in an attested TEE.

***

## Proxies

Redaction proxy configurations for upstream LLM providers, consumed by [`POST /api/redact/chat/completions`](#openai-compatible-chat-completions-with-pii-redaction). Auth: Privy bearer token (preferred); the `identifier` query parameter / body field is a dev fallback. The stored upstream key is never returned by the API.

### List proxies

`GET /api/proxies`

```bash
curl https://app.trezalabs.com/api/proxies \
  -H "Authorization: Bearer <privy-access-token>"
```

**Response** `200`

```json
{
  "proxies": [
    {
      "id": "proxy_1234567890_abc123",
      "accountId": "acct_1234567890_abc123",
      "name": "openai proxy",
      "upstreamProvider": "openai",
      "upstreamUrl": "https://api.openai.com/v1/chat/completions",
      "redactionPolicy": { "entities": [] },
      "status": "active",
      "hasUpstreamKey": true,
      "createdAt": "2026-06-01T00:00:00Z",
      "updatedAt": "2026-06-01T00:00:00Z",
      "lastRequestAt": "2026-06-10T09:00:00Z"
    }
  ]
}
```

An empty `redactionPolicy.entities` array means everything detected is redacted.

### Create a proxy

`POST /api/proxies`

| Body field         | Required | Description                                                                                                                   |
| ------------------ | -------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `upstreamProvider` | Yes      | `openai`, `azure-openai`, `anthropic`, or `custom`                                                                            |
| `name`             | No       | Display name (defaults to `<provider> proxy`)                                                                                 |
| `upstreamUrl`      | No       | Upstream chat completions URL. Required for `azure-openai` and `custom`; defaults to the provider's public endpoint otherwise |
| `entities`         | No       | Entity types to redact (empty = redact everything detected). Requires the `customPolicies` plan feature                       |
| `upstreamKey`      | No       | Upstream provider API key, stored encrypted; never returned by the API                                                        |
| `identifier`       | No       | Fallback account identifier when no Privy bearer token is supplied                                                            |

```bash
curl -X POST https://app.trezalabs.com/api/proxies \
  -H "Authorization: Bearer <privy-access-token>" \
  -H "Content-Type: application/json" \
  -d '{ "upstreamProvider": "openai", "name": "openai proxy", "upstreamKey": "sk-..." }'
```

**Response** `201` — `{ "proxy": { ... } }`. `402` `PLAN_FEATURE`/`PLAN_LIMIT` when the plan does not allow non-OpenAI providers, custom policies, or more proxies.

### Update a proxy

`PUT /api/proxies`

| Body field            | Required | Description                                                             |
| --------------------- | -------- | ----------------------------------------------------------------------- |
| `id`                  | Yes      | Proxy ID                                                                |
| `name`, `upstreamUrl` | No       | Fields to update                                                        |
| `status`              | No       | `active` or `paused`                                                    |
| `entities`            | No       | New redaction policy (requires `customPolicies` feature when non-empty) |
| `upstreamKey`         | No       | New upstream key to store (encrypted)                                   |
| `clearUpstreamKey`    | No       | Set `true` to remove the stored upstream key                            |
| `identifier`          | No       | Fallback account identifier                                             |

```bash
curl -X PUT https://app.trezalabs.com/api/proxies \
  -H "Authorization: Bearer <privy-access-token>" \
  -H "Content-Type: application/json" \
  -d '{ "id": "proxy_1234567890_abc123", "status": "paused" }'
```

**Response** `200` — `{ "proxy": { ... } }`. `402` `PLAN_FEATURE` if custom redaction policies require a higher plan, `404` if not found or access denied.

### Delete a proxy

`DELETE /api/proxies`

| Query parameter | Required | Description                 |
| --------------- | -------- | --------------------------- |
| `id`            | Yes      | Proxy ID to delete          |
| `identifier`    | No       | Fallback account identifier |

```bash
curl -X DELETE "https://app.trezalabs.com/api/proxies?id=proxy_1234567890_abc123" \
  -H "Authorization: Bearer <privy-access-token>"
```

**Response** `200` — `{ "message": "Proxy deleted" }`.

### Preview a redaction policy

`POST /api/proxies/test`

Dashboard helper: preview what a proxy's redaction policy would strip from a sample text. Makes no upstream call and is not billed.

| Body field   | Required | Description                                                              |
| ------------ | -------- | ------------------------------------------------------------------------ |
| `text`       | Yes      | Sample text to redact                                                    |
| `proxyId`    | No       | Apply this proxy's redaction policy (omit to redact everything detected) |
| `identifier` | No       | Fallback account identifier                                              |

```bash
curl -X POST https://app.trezalabs.com/api/proxies/test \
  -H "Authorization: Bearer <privy-access-token>" \
  -H "Content-Type: application/json" \
  -d '{ "text": "Call me at 555-0100", "proxyId": "proxy_1234567890_abc123" }'
```

**Response** `200` — `{ "redacted": "…", "entities": [ ... ], "entityCount": 1, "mode": "standard", "modelVersion": "…" }` (plus `attestation` on TEE-enabled plans). `404` if the proxy is not found.

***

## Billing

### Get plan catalog

`GET /api/billing/plans`

Public plan catalog for the pricing UI. No authentication required.

```bash
curl https://app.trezalabs.com/api/billing/plans
```

**Response** `200`

```json
{
  "plans": [
    {
      "id": "pro",
      "name": "Pro",
      "description": "…",
      "basePriceMonthly": 0,
      "overagePerRequest": 0,
      "trialPeriodDays": 14,
      "selfServe": true,
      "recommended": true,
      "purchasable": true,
      "entitlements": {
        "maxProxies": 5,
        "includedRequests": 10000,
        "meteredOverage": true,
        "features": {
          "usageInsights": true,
          "customPolicies": true,
          "multipleProviders": true,
          "teeAttestation": false,
          "enhancedRedactionEngine": true,
          "attestationReports": false,
          "auditExport": true
        }
      }
    }
  ]
}
```

`maxProxies: null` means unlimited.

### Get current subscription and entitlements

`GET /api/billing/subscription`

Auth: Privy bearer token (preferred); `identifier` query is a dev fallback.

```bash
curl https://app.trezalabs.com/api/billing/subscription \
  -H "Authorization: Bearer <privy-access-token>"
```

**Response** `200`

```json
{
  "subscription": {
    "accountId": "acct_1234567890_abc123",
    "planId": "pro",
    "status": "active",
    "currentPeriodStart": "2026-06-01T00:00:00Z",
    "currentPeriodEnd": "2026-07-01T00:00:00Z",
    "cancelAtPeriodEnd": false
  },
  "entitlements": {
    "planId": "pro",
    "status": "active",
    "active": true,
    "entitlements": { "maxProxies": 5, "includedRequests": 10000, "meteredOverage": true, "features": { } },
    "usage": { "requestsThisPeriod": 1200, "includedRequests": 10000, "overLimit": false }
  }
}
```

Subscription `status` is one of `active`, `trialing`, `past_due`, `canceled`, `unpaid`, `incomplete`, `incomplete_expired`, `none`. `subscription` is `null` for accounts that have never subscribed.

### Create a Stripe Checkout session

`POST /api/billing/checkout`

Creates a Stripe Checkout Session for a self-serve plan (the Stripe customer is created on first use).

| Body field    | Required | Description                                                                                            |
| ------------- | -------- | ------------------------------------------------------------------------------------------------------ |
| `planId`      | Yes      | Self-serve plan to subscribe to, e.g. `pro`                                                            |
| `identifier`  | No       | Fallback account identifier                                                                            |
| `gaClientId`  | No       | GA4 client ID for conversion attribution                                                               |
| `attribution` | No       | First-touch attribution map (`utm_source`, `utm_campaign`, `utm_content`, `gclid`, `gbraid`, `wbraid`) |

```bash
curl -X POST https://app.trezalabs.com/api/billing/checkout \
  -H "Authorization: Bearer <privy-access-token>" \
  -H "Content-Type: application/json" \
  -d '{ "planId": "pro" }'
```

**Response** `200` — `{ "url": "https://checkout.stripe.com/…", "sessionId": "…" }`. Redirect the user to `url`. `400` for an unknown or non-self-serve plan, `503` when billing is not configured.

### Create a Stripe Billing Portal session

`POST /api/billing/portal`

Create a Stripe Billing Portal session so a customer can manage or cancel their subscription. The body is optional (`identifier` fallback only).

```bash
curl -X POST https://app.trezalabs.com/api/billing/portal \
  -H "Authorization: Bearer <privy-access-token>"
```

**Response** `200` — `{ "url": "https://billing.stripe.com/…" }`. `400` when the account has no billing profile yet.

### Stripe webhook receiver

`POST /api/billing/webhook`

Receives Stripe subscription lifecycle events. The request body must be the raw Stripe event payload, verified against the `stripe-signature` header. Not intended for direct client use — configure it as the webhook endpoint in Stripe. Returns `200` `{ "received": true }` on success, `400` for a missing/invalid signature.

***

## Account

### Bootstrap the current account

`GET /api/account`

Returns (and creates on first call) the caller's account, with subscription and entitlements. Auth: Privy bearer token (preferred); `identifier` query is a dev fallback.

```bash
curl https://app.trezalabs.com/api/account \
  -H "Authorization: Bearer <privy-access-token>"
```

**Response** `200`

```json
{
  "account": {
    "id": "acct_1234567890_abc123",
    "ownerIdentifier": "user@company.com",
    "name": "…",
    "createdAt": "2026-06-01T00:00:00Z",
    "updatedAt": "2026-06-01T00:00:00Z"
  },
  "subscription": { },
  "entitlements": { }
}
```

***

## Usage & Reports

### Get redaction usage insights

`GET /api/usage`

Redaction-proxy usage insights: request volume over time, per-proxy totals, PII entities redacted by type, and recent redactions. Defaults to the last 30 days. Auth: Privy bearer token (preferred); `account`/`identifier` query is a dev fallback.

| Query parameter | Required | Description                                              |
| --------------- | -------- | -------------------------------------------------------- |
| `account`       | No       | Fallback account identifier (`identifier` also accepted) |
| `days`          | No       | Time window in days, 1–90 (default 30)                   |

```bash
curl "https://app.trezalabs.com/api/usage?days=30" \
  -H "Authorization: Bearer <privy-access-token>"
```

**Response** `200`

```json
{
  "totalRequests": 1200,
  "meteredRequests": 1200,
  "totalEntitiesRedacted": 3400,
  "series": [ { "date": "2026-06-01", "count": 40 } ],
  "byProxy": { "proxy_1234567890_abc123": 800 },
  "entitiesByType": { "EMAIL": 2100, "PHONE": 900 },
  "recent": [
    { "requestId": "…", "ts": "2026-06-10T09:00:00Z", "source": "proxy", "counts": { "EMAIL": 2 } }
  ],
  "entitlements": { }
}
```

`recent` contains up to 25 most recent redactions; `source` is `run` or `proxy`.

### Export redaction audit log as CSV

`GET /api/usage/export`

CSV export of redaction audit entries for the signed-in account. Requires the `auditExport` plan feature (Pro or Enterprise) — `403` with code `PLAN_FEATURE` otherwise.

| Query parameter | Required | Description                            |
| --------------- | -------- | -------------------------------------- |
| `identifier`    | No       | Fallback account identifier            |
| `days`          | No       | Time window in days, 1–90 (default 30) |

```bash
curl -L -o audit.csv "https://app.trezalabs.com/api/usage/export?days=30" \
  -H "Authorization: Bearer <privy-access-token>"
```

**Response** `200` — `text/csv` file download.

### List ASE reports

`GET /api/reports`

List cached Attestation of Secure Execution (ASE) documents for all of an account's enclaves, newest first.

| Query parameter | Required | Description                                |
| --------------- | -------- | ------------------------------------------ |
| `account`       | Yes      | Account identifier (legacy alias `wallet`) |

```bash
curl "https://app.trezalabs.com/api/reports?account=user@company.com" \
  -H "Authorization: Bearer <privy-access-token>"
```

**Response** `200`

```json
{
  "reports": [
    {
      "enclaveId": "enc_123456",
      "enclaveName": "Trading Bot Enclave",
      "enclaveStatus": "DEPLOYED",
      "region": "us-east-1",
      "providerId": "aws-nitro",
      "aseGeneratedAt": "2026-06-01T00:00:00Z",
      "ase": { }
    }
  ]
}
```

`ase` is the full [ASE document](#get-ase-document).

***

## Contact

### Submit the contact form

`POST /api/contact`

Send a contact form submission to the Treza team. No authentication required.

| Body field | Required | Description        |
| ---------- | -------- | ------------------ |
| `name`     | Yes      | Your name          |
| `email`    | Yes      | Your email address |
| `company`  | No       | Company name       |
| `message`  | Yes      | Message body       |

```bash
curl -X POST https://app.trezalabs.com/api/contact \
  -H "Content-Type: application/json" \
  -d '{ "name": "Jane Doe", "email": "jane@example.com", "message": "Hello Treza" }'
```

**Response** `200` — `{ "success": true, "id": "…" }` (`id` is the email delivery ID). `400` when name, email, or message is missing.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.trezalabs.com/developers/rest-api.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
