> ## Documentation Index
> Fetch the complete documentation index at: https://docs.impellabs.tech/llms.txt
> Use this file to discover all available pages before exploring further.

# Your first response

> One call to /api/v2/responses, then the same call streamed.

<Steps>
  <Step title="Get a key with the scopes you need">
    An operator issues a workspace key (`tgcc_…`). For this page it needs
    `responses:create`. The secret is shown once, at creation.

    ```bash theme={null}
    export IMPEL_KEY="tgcc_xxxxxxxxxxxx"
    export IMPEL_API="https://api.impellabs.tech/api/v2"
    ```
  </Step>

  <Step title="Ask an agent for something">
    ```bash theme={null}
    curl -sS "$IMPEL_API/responses" \
      -H "Authorization: Bearer $IMPEL_KEY" \
      -H "Content-Type: application/json" \
      -H "Idempotency-Key: $(uuidgen)" \
      -d '{
        "agent": "agt_your_assistant",
        "input": "In two sentences: what changed in our returns policy?"
      }'
    ```

    `POST /responses` is stateless from your side. No conversation is named and
    none comes back — the request leaves a record that it happened and what it
    cost, and no copy of what was said.
  </Step>

  <Step title="Read the response">
    ```json theme={null}
    {
      "id": "run_01J8Z...",
      "status": "completed",
      "output": [
        { "type": "text", "text": "Returns now close after 30 days…" }
      ],
      "request_id": "req_01J8Z...",
      "trace_id": "trc_01J8Z...",
      "usage": {
        "input_tokens": 812,
        "output_tokens": 64,
        "cached_tokens": 0,
        "total_tokens": 876
      },
      "agent": "agt_your_assistant",
      "agent_version": 7
    }
    ```

    `output` is an **array**, not a string. A run can produce more than one
    thing — prose plus a structured object, or a refusal — and a contract that
    starts as a string has nowhere to put the second one. Item types are `text`,
    `json` and `refusal`.

    Quote `request_id` in a support ticket. It is also on the `X-Request-Id`
    response header, including on responses that carry no body.
  </Step>

  <Step title="Stream the same call">
    Set `"stream": true` and the body becomes `text/event-stream`. Nothing else
    about the request changes.

    ```bash theme={null}
    curl -sS -N "$IMPEL_API/responses" \
      -H "Authorization: Bearer $IMPEL_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "agent": "agt_your_assistant",
        "input": "In two sentences: what changed in our returns policy?",
        "stream": true
      }'
    ```

    ```
    id: 1
    event: response.started
    data: {"response_id":"run_01J8Z...","status":"running","stream_version":"2026-09-07",...}

    id: 2
    event: response.output_text.delta
    data: {"delta":"Returns now close "}

    : ping

    id: 9
    event: response.completed
    data: {"id":"run_01J8Z...","status":"completed","output":[...],"usage":{...}}
    ```

    Exactly one terminal event is emitted per stream, ever. Read the whole
    output off `response.completed` — see [Streaming](/v2/streaming) for why the
    deltas are advisory and the completion event is authoritative.
  </Step>

  <Step title="Look at what it actually did">
    ```bash theme={null}
    curl -sS "$IMPEL_API/logs/run_01J8Z..." \
      -H "Authorization: Bearer $IMPEL_KEY"
    ```

    Needs `logs:read`. You get status, latency, model, tokens, tool names and
    error code — the *shape* of the execution. Prompt assembly and identity
    steps do not appear at all.
  </Step>
</Steps>

## Handle four things and you have a working client

<AccordionGroup>
  <Accordion title="Errors — switch on `code`, never on `message`">
    ```json theme={null}
    { "error": { "code": "usage_blocked", "message": "…", "request_id": "req_…" } }
    ```

    `message` is prose and may be reworded without a version bump. `code` is a
    closed vocabulary of seventeen values. [Errors](/v2/errors)
  </Accordion>

  <Accordion title="402 and 429 are not failures">
    `usage_blocked` (402) and `rate_limited` (429) end a run as `blocked`, not
    `failed`. Retrying a 402 will never help; retrying a 429 after a delay will.
  </Accordion>

  <Accordion title="Send an Idempotency-Key on every write">
    Same key and same body replays the stored response including its status.
    Same key and a different body is a **409**, never a replay. The window is 24
    hours. [Idempotency](/v2/idempotency)
  </Accordion>

  <Accordion title="Do not build a cursor">
    Cursors are opaque base64url. A raw ISO timestamp is not a cursor: `+00:00`
    decodes to a space in a query string, which silently breaks every second
    page.
  </Accordion>
</AccordionGroup>

## Where to go next

<CardGroup cols={2}>
  <Card title="Durable conversations" icon="comments" href="/v2/conversations">
    When you want a transcript rather than a one-shot answer.
  </Card>

  <Card title="Structured outputs" icon="brackets-curly" href="/v2/structured-outputs">
    Get JSON that validates against your schema, or a 422 that says why not.
  </Card>

  <Card title="Documents and extractions" icon="file-lines" href="/v2/documents">
    Upload a file, then pull validated fields out of it with evidence.
  </Card>

  <Card title="Async jobs" icon="clock" href="/v2/jobs">
    What a 202 means, and how to poll it without writing a `while True`.
  </Card>
</CardGroup>
