{
  "info": {
    "_postman_id": "e1a2c3d4-0001-4a00-9000-enrichapiv1col",
    "name": "Enrich API (v1)",
    "description": "Official Postman collection for the Enrich external API.\n\nRun the same AI-driven identity / KYC / business verifications programmatically — durable runs, exact billing, real-time streams. Full reference: API_SPEC.md · /docs/api · Enrich-API-Documentation.pdf.\n\n## Setup\n1. Open the collection **Variables** tab.\n2. Set `baseUrl` to your Enrich host (e.g. https://enrich.timble.ai).\n3. Set `apiKey` to a key from **Admin → API Keys** (looks like `sk-tm-…`).\n4. Send **Runs → Create a run** — its test script captures `runId` and `threadId` automatically, so every other request just works.\n\n## Auth\nAll `/v1/*` requests use Bearer auth with `{{apiKey}}` (set at the collection level). The **Admin** folder uses a session JWT (`{{adminJwt}}`) instead — those are web-admin endpoints, not part of the key-authed API.",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "auth": {
    "type": "bearer",
    "bearer": [{ "key": "token", "value": "{{apiKey}}", "type": "string" }]
  },
  "variable": [
    { "key": "baseUrl", "value": "https://enrich.timble.ai", "type": "string" },
    { "key": "apiKey", "value": "sk-tm-REPLACE_ME", "type": "string" },
    { "key": "runId", "value": "", "type": "string" },
    { "key": "threadId", "value": "", "type": "string" },
    { "key": "adminJwt", "value": "", "type": "string" }
  ],
  "item": [
    {
      "name": "Runs",
      "item": [
        {
          "name": "Create a run",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "// Capture ids so the rest of the collection just works.",
                  "if (pm.response.code === 202 || pm.response.code === 200) {",
                  "  const j = pm.response.json();",
                  "  if (j.id) { pm.collectionVariables.set('runId', j.id); }",
                  "  if (j.thread_id) { pm.collectionVariables.set('threadId', j.thread_id); }",
                  "  pm.test('run created', () => pm.expect(j.id).to.be.a('string'));",
                  "} else {",
                  "  pm.test('unexpected status: ' + pm.response.code, () => false);",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              {
                "key": "Idempotency-Key",
                "value": "{{$guid}}",
                "description": "Unique per logical request. Replaying the same key + body returns the same run (200) instead of executing again — never a duplicate charge. {{$guid}} generates a fresh one each send; pin a real value (e.g. your order id) in production."
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"message\": \"Verify identity: Rahul Sharma, +91 98xxxxxx10, PAN ABCDE1234F\",\n  \"workflow\": \"kyc-verification\",\n  \"metadata\": { \"order_id\": \"7841\" }\n}"
            },
            "url": {
              "raw": "{{baseUrl}}/v1/runs",
              "host": ["{{baseUrl}}"],
              "path": ["v1", "runs"]
            },
            "description": "Start a verification. Returns 202 with the run resource (execution continues in the background).\n\nBody fields:\n- `message` (required) — the request in plain language, with every identifier you have.\n- `workflow` (optional) — a built-in or org workflow slug (see Workflows → List).\n- `thread_id` (optional) — continue an existing conversation.\n- `stream` (optional) — set true for an SSE response (see 'Create a run (streaming)').\n- `metadata` (optional) — ≤2KB of your own data, echoed back on every read."
          }
        },
        {
          "name": "Create a run (streaming)",
          "request": {
            "method": "POST",
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"message\": \"Verify identity: Rahul Sharma, +91 98xxxxxx10, PAN ABCDE1234F\",\n  \"workflow\": \"kyc-verification\",\n  \"stream\": true\n}"
            },
            "url": {
              "raw": "{{baseUrl}}/v1/runs",
              "host": ["{{baseUrl}}"],
              "path": ["v1", "runs"]
            },
            "description": "Same as 'Create a run' but `stream: true` upgrades the response to a Server-Sent Events stream: run.created → tool_call / answer.delta / run.report → run.completed. Postman shows the raw event stream; for production use an EventSource/SSE client. The first event (run.created) carries the run_id."
          }
        },
        {
          "name": "Retrieve a run (long-poll)",
          "request": {
            "method": "GET",
            "url": {
              "raw": "{{baseUrl}}/v1/runs/{{runId}}?wait=50",
              "host": ["{{baseUrl}}"],
              "path": ["v1", "runs", "{{runId}}"],
              "query": [
                {
                  "key": "wait",
                  "value": "50",
                  "description": "Long-poll seconds (max 50). Returns early the moment the status changes. Omit for an instant snapshot."
                }
              ]
            },
            "description": "Fetch the run's state and result. Poll this until the status is terminal (completed / failed / cancelled / interrupted). On completion, read `answer`, `report`, `credits_used`, and the per-check `tool_calls`."
          }
        },
        {
          "name": "Stream run events (re-attach)",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Last-Event-ID",
                "value": "",
                "description": "Optional — the id of the last event you processed. Replays only what you missed. Leave blank to replay from the start.",
                "disabled": true
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/v1/runs/{{runId}}/events",
              "host": ["{{baseUrl}}"],
              "path": ["v1", "runs", "{{runId}}", "events"]
            },
            "description": "SSE stream of the run's full history + live progress. Honours Last-Event-ID for gapless reconnects. A finished run replays all its events, then closes."
          }
        },
        {
          "name": "Resolve plan approval",
          "request": {
            "method": "POST",
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"decision\": \"approved\"\n}"
            },
            "url": {
              "raw": "{{baseUrl}}/v1/runs/{{runId}}/approval",
              "host": ["{{baseUrl}}"],
              "path": ["v1", "runs", "{{runId}}", "approval"]
            },
            "description": "Valid only while the run is `requires_approval`. `approved` resumes execution; `declined` cancels it (only planning work is billed). Anything else → 409 not_awaiting_approval."
          }
        },
        {
          "name": "Cancel a run",
          "request": {
            "method": "POST",
            "url": {
              "raw": "{{baseUrl}}/v1/runs/{{runId}}/cancel",
              "host": ["{{baseUrl}}"],
              "path": ["v1", "runs", "{{runId}}", "cancel"]
            },
            "description": "Stop a queued / running / requires_approval run. Work already performed is billed; partial answers are preserved. A terminal run → 409 not_cancellable."
          }
        }
      ]
    },
    {
      "name": "Threads & workflows",
      "item": [
        {
          "name": "List runs in a thread",
          "request": {
            "method": "GET",
            "url": {
              "raw": "{{baseUrl}}/v1/threads/{{threadId}}/runs?limit=20&offset=0",
              "host": ["{{baseUrl}}"],
              "path": ["v1", "threads", "{{threadId}}", "runs"],
              "query": [
                { "key": "limit", "value": "20", "description": "Max 100." },
                { "key": "offset", "value": "0" }
              ]
            },
            "description": "All runs in a thread, newest first."
          }
        },
        {
          "name": "List workflows",
          "request": {
            "method": "GET",
            "url": {
              "raw": "{{baseUrl}}/v1/workflows",
              "host": ["{{baseUrl}}"],
              "path": ["v1", "workflows"]
            },
            "description": "Every workflow your key can pass as `workflow`: built-ins (scope `built-in`) plus your org's custom procedures (scope `org`). Custom workflows are managed in the web app under Admin → Workflows."
          }
        }
      ]
    },
    {
      "name": "Usage",
      "item": [
        {
          "name": "Get credit balance",
          "request": {
            "method": "GET",
            "url": {
              "raw": "{{baseUrl}}/v1/usage",
              "host": ["{{baseUrl}}"],
              "path": ["v1", "usage"]
            },
            "description": "Org credit balance. `credits_remaining` may be negative (overdraft owed) or null (unlimited plan)."
          }
        }
      ]
    },
    {
      "name": "Admin — manage keys & workflows (session JWT)",
      "description": "Web-admin endpoints, NOT part of the key-authed API. They authenticate with a session JWT (`{{adminJwt}}`), not an sk-tm key. Normally you use the Enrich web UI for these; included here for automation/testing. Set `adminJwt` in the collection variables (an org-admin session token).",
      "auth": {
        "type": "bearer",
        "bearer": [{ "key": "token", "value": "{{adminJwt}}", "type": "string" }]
      },
      "item": [
        {
          "name": "Create API key",
          "request": {
            "method": "POST",
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": { "mode": "raw", "raw": "{\n  \"name\": \"production\"\n}" },
            "url": {
              "raw": "{{baseUrl}}/api-keys",
              "host": ["{{baseUrl}}"],
              "path": ["api-keys"]
            },
            "description": "Returns the full `sk-tm-…` secret once. Copy it into the collection's `apiKey` variable to use the /v1 requests."
          }
        },
        {
          "name": "List API keys",
          "request": {
            "method": "GET",
            "url": { "raw": "{{baseUrl}}/api-keys", "host": ["{{baseUrl}}"], "path": ["api-keys"] },
            "description": "Lists keys (prefixes only — secrets are never returned again)."
          }
        },
        {
          "name": "Revoke API key",
          "request": {
            "method": "DELETE",
            "url": {
              "raw": "{{baseUrl}}/api-keys/:keyId",
              "host": ["{{baseUrl}}"],
              "path": ["api-keys", ":keyId"],
              "variable": [{ "key": "keyId", "value": "1" }]
            },
            "description": "Immediately revokes a key."
          }
        },
        {
          "name": "List workflows (admin)",
          "request": {
            "method": "GET",
            "url": { "raw": "{{baseUrl}}/workflows", "host": ["{{baseUrl}}"], "path": ["workflows"] },
            "description": "Org's custom workflows (full content), for management."
          }
        },
        {
          "name": "Create workflow",
          "request": {
            "method": "POST",
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"slug\": \"loan-pre-check\",\n  \"name\": \"Loan applicant pre-check\",\n  \"description\": \"PAN + phone + employment, then report\",\n  \"content_md\": \"# Loan applicant pre-check\\n\\n## Phase 1 — Identity\\n- Verify PAN against the provided name\\n- Run mobile intelligence on the phone number\\n\\n## Report\\n- Emphasise identity mismatches and credit-risk signals\"\n}"
            },
            "url": { "raw": "{{baseUrl}}/workflows", "host": ["{{baseUrl}}"], "path": ["workflows"] },
            "description": "Create a custom workflow. The slug becomes usable as `workflow` in /v1/runs immediately. Slug: lowercase letters/digits/hyphens, unique per org."
          }
        },
        {
          "name": "Update workflow",
          "request": {
            "method": "PUT",
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"name\": \"Loan applicant pre-check (v2)\",\n  \"content_md\": \"# Updated procedure\\n…\"\n}"
            },
            "url": {
              "raw": "{{baseUrl}}/workflows/:workflowId",
              "host": ["{{baseUrl}}"],
              "path": ["workflows", ":workflowId"],
              "variable": [{ "key": "workflowId", "value": "1" }]
            }
          }
        },
        {
          "name": "Deactivate workflow",
          "request": {
            "method": "DELETE",
            "url": {
              "raw": "{{baseUrl}}/workflows/:workflowId",
              "host": ["{{baseUrl}}"],
              "path": ["workflows", ":workflowId"],
              "variable": [{ "key": "workflowId", "value": "1" }]
            },
            "description": "Soft-delete (reactivate by editing). The slug stops resolving in /v1/runs."
          }
        }
      ]
    }
  ]
}
