---
title: "Get a function test run trace"
method: GET
path: "/functions/{id}/test-runs/{run_id}/trace"
tags: ["Functions"]
---

# Get a function test run trace

`GET /functions/{id}/test-runs/{run_id}/trace`

Returns the current end-to-end trace for a function test run.
The trace is intentionally partial while the test is still in
flight: callers can poll this endpoint and watch it fill in
from send -> inbound -> webhook deliveries -> outbound
requests, logs, and replies.

## Response `200`

Function test run trace

- object
  - `success` true, required
  - `data` object, required — End-to-end trace for a `POST /functions/{id}/test` run. The shape is stable, but many nested sections are null or empty until the corresponding phase has happened.
    - `state` 'send_failed' | 'waiting_for_send' | 'waiting_for_inbound' | 'waiting_for_function' | 'completed' | 'failed', required — High-level state for a function test run trace: - `send_failed`: the initial test email send failed. - `waiting_for_send`: the test run was created but no send result has been recorded yet. - `waiting_for_inbound`: the test send was queued and the matching inbound email has not arrived yet. - `waiting_for_function`: the inbound email arrived and webhook/function processing is still in flight. - `completed`: the function webhook completed successfully. - `failed`: webhook delivery exhausted retries.
    - `test_run` object, required
      - `id` string, uuid, required
      - `function_id` string, uuid, required
      - `inbound_domain` string, required
      - `to` string, required
      - `from` string, required
      - `subject` string, required
      - `poll_since` string, date-time, required
      - `created_at` string, date-time, required
      - `sent_at` string, date-time, nullable, required
      - `send_error` string, nullable, required
    - `test_send` object, nullable, required
      - `id` string, uuid, required
      - `status` 'queued' | 'submitted_to_agent' | 'agent_failed' | 'gate_denied' | 'unknown' | 'delivered' | 'bounced' | 'deferred' | 'wait_timeout', required — Lifecycle status of a sent_emails row. Possible values: - `queued`: pre-call INSERT; the outbound agent has not yet replied. - `submitted_to_agent`: agent accepted; `queue_id` is set. - `agent_failed`: agent rejected; `error_code` and `error_message` carry the reason. - `gate_denied`: a recipient-scope gate denied the send; the agent was never called. The `gates` array carries the denial detail. /send-mail returns 403 in this case so callers see the denial synchronously; /sent-emails additionally records the row for historical lookup, which is when this status appears in a listing. - `unknown`: terminal indeterminate; the on-box log poller couldn't classify the receiver's response. - `delivered` / `bounced` / `deferred` / `wait_timeout`: terminal delivery outcomes (see DeliveryStatus).
      - `queue_id` string, nullable, required
      - `created_at` string, date-time, required
      - `updated_at` string, date-time, required
    - `inbound_email` object, nullable, required
      - `id` string, uuid, required
      - `status` 'pending' | 'accepted' | 'completed' | 'rejected', required — Lifecycle status of an INBOUND email (a row in the `emails` table). Distinct from `SentEmailStatus`, which describes the OUTBOUND lifecycle (the `sent_emails` table) and uses a different vocabulary because the lifecycles differ. Possible values: - `pending`: the row was inserted at ingestion (mx_main) and has not yet completed the spam / filter / auth pipeline. Body and parsed fields are present; webhook delivery is not yet scheduled. Most rows transition out of `pending` within seconds. - `accepted`: the inbound passed the policy gates and is queued for webhook delivery. The `webhook_status` field tracks the separate webhook-delivery lifecycle from this point. - `completed`: terminal success. Webhook delivery attempted and acknowledged by every active endpoint, OR no endpoints are configured, so the row is durably archived. - `rejected`: terminal failure at ingestion (spam, blocked sender, filter rule, malformed). The body and metadata are stored for auditing but no webhook fires and the row is not repliable. See also `webhook_status` (separate enum tracking the webhook-delivery state machine) and `SentEmailStatus` (the outbound vocabulary).
      - `received_at` string, date-time, required
      - `from` string, required
      - `to` string, required
      - `subject` string, nullable, required
      - `webhook_status` 'pending' | 'in_flight' | 'fired' | 'failed' | 'exhausted' | 'null', nullable, required — Webhook-delivery state for an inbound email. Tracks a SEPARATE lifecycle from the email's `status` field; the same row carries both. Possible values: - `pending`: ingestion is past `pending` (the email itself is `accepted`) but the webhook fan-out has not yet started for this row. - `in_flight`: at least one delivery attempt is in flight. - `fired`: terminal success. Every active endpoint acknowledged the delivery (or accepted it after retries). - `failed`: terminal partial-failure. At least one endpoint exhausted its retry budget; some endpoints may still have succeeded. - `exhausted`: terminal failure. Every endpoint exhausted its retry budget without success. - `null`: no endpoints configured, so no webhook lifecycle applies. Note that the value `pending` here does NOT mean the email is `pending`; it means the email is past ingestion but webhook delivery has not yet begun. Two overlapping uses of the word `pending` for distinct lifecycle phases.
      - `webhook_attempt_count` integer, required
      - `webhook_last_status_code` integer, nullable, required
      - `webhook_last_error` string, nullable, required
    - `deliveries` object[], required
      - `id` string, required — Webhook delivery id.
      - `endpoint_id` string, uuid, required
      - `endpoint_url` string, uri, required
      - `status` 'pending' | 'delivered' | 'header_confirmed' | 'failed', required
      - `attempt_count` integer, required
      - `duration_ms` integer, nullable, required
      - `last_error` string, nullable, required
      - `last_error_code` string, nullable, required
      - `created_at` string, date-time, required
      - `updated_at` string, date-time, required
      - `endpoint` object, nullable, required
        - `id` string, uuid, required
        - `kind` string, required — Endpoint kind. Current traces may include `http` or `function`; future endpoint kinds may appear.
        - `function_id` string, uuid, nullable, required
        - `function_name` string, nullable, required
        - `domain_id` string, uuid, nullable, required
        - `enabled` boolean, required
        - `deactivated_at` string, date-time, nullable, required
        - `is_current_function` boolean, required
    - `outbound_requests` object[], required
      - `id` string, uuid, required
      - `function_id` string, uuid, required
      - `webhook_delivery_id` string, nullable, required
      - `email_id` string, uuid, nullable, required
      - `endpoint_id` string, uuid, nullable, required
      - `method` string, required
      - `url` string, uri, required
      - `host` string, required
      - `path` string, required
      - `status_code` integer, nullable, required
      - `ok` boolean, nullable, required
      - `duration_ms` integer, required
      - `error` string, nullable, required
      - `ts` string, date-time, required
    - `logs` object[], required
      - `id` string, uuid, required — Unique log row id (stable across pages).
      - `function_id` string, uuid, required — The function this log row belongs to.
      - `level` 'debug' | 'log' | 'info' | 'warn' | 'error', required — Severity. `log` is the runtime's default for unannotated `console.log` calls; the other levels match standard `console.*` methods.
      - `message` string, required — The textual message body. The runtime stringifies non-string arguments before persisting, so this is always a plain string.
      - `ts` string, date-time, required — When the handler emitted this line. Newest-first ordering on this column drives pagination; clock is the runtime's, not the gateway's.
      - `metadata` object, nullable — Optional structured payload the runtime attaches alongside the message (e.g. extra args passed to `console.log`). Shape is opaque; treat keys as untyped.
    - `replies` object[], required
      - `id` string, uuid, required
      - `status` 'queued' | 'submitted_to_agent' | 'agent_failed' | 'gate_denied' | 'unknown' | 'delivered' | 'bounced' | 'deferred' | 'wait_timeout', required — Lifecycle status of a sent_emails row. Possible values: - `queued`: pre-call INSERT; the outbound agent has not yet replied. - `submitted_to_agent`: agent accepted; `queue_id` is set. - `agent_failed`: agent rejected; `error_code` and `error_message` carry the reason. - `gate_denied`: a recipient-scope gate denied the send; the agent was never called. The `gates` array carries the denial detail. /send-mail returns 403 in this case so callers see the denial synchronously; /sent-emails additionally records the row for historical lookup, which is when this status appears in a listing. - `unknown`: terminal indeterminate; the on-box log poller couldn't classify the receiver's response. - `delivered` / `bounced` / `deferred` / `wait_timeout`: terminal delivery outcomes (see DeliveryStatus).
      - `to` string, required
      - `subject` string, required
      - `queue_id` string, nullable, required
      - `created_at` string, date-time, required

## Other responses

- `400` — Invalid request parameters
- `401` — Invalid or missing API key
- `403` — Authenticated caller lacks permission for the operation
- `404` — Resource not found

---

[API](https://skmtc.dev/primitive/apis/primitive-api.md) · [All operations](https://skmtc.dev/primitive/apis/primitive-api/llms.txt) · [OpenAPI document](https://skmtc-service-production.skmtc.workers.dev/v1/apis/primitive/primitive-api/revisions/c2299d28022c/schema)
