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

# Webhooks

## Resend a webhook event

> Sends a previously delivered event again, to your current webhook URL, signed with your current signing secret and rendered in your pinned version. The payload is rebuilt from the stored event, so it carries the status the event originally announced, not the order's current status, and a fresh \`timestamp\`. The event keeps its \`event\_id\`: an endpoint that dedupes on it treats the resend as the event it already processed. Delivery is asynchronous, so 202 means the event was queued, not that it arrived; find the outcome with \`GET /api/v1/webhook-deliveries\`, where the resend replaces the event's earlier outcome. A delivered event can be resent for 30 days; an older one, or one delivered before Vantage began keeping delivered events, returns 404 with code \`event\_not\_resendable\`. Pass \`dry\_run=true\` to check that an event can be resent without sending it.

```json
{"openapi":"3.1.0","info":{"title":"Terra Vantage API","version":"2026.10.22"},"servers":[{"url":"https://vantage.tryterra.co"}],"security":[{"BasicAuth":[]},{"DataBearer":[],"DevIDHeader":[]}],"components":{"securitySchemes":{"BasicAuth":{"type":"http","scheme":"basic","description":"Authenticate with your Terra API credentials: username = your Terra\n`dev-id`, password = your Terra API key — the same credentials used for\nevery other Terra API. The header pair `dev-id` + `x-api-key` is also\naccepted. Suspended credentials receive 403; accounts without the\nVantage entitlement receive 403 once entitlement enforcement is\nenabled. Once the credential validator is configured, backfilled keys\nalso honor current token expiry, revocation, audience and Vantage scopes.\nInvalid tokens receive 401, insufficient scopes 403 and an unavailable\nvalidator 503. These checks remain active when product policy is off.\n"},"DataBearer":{"type":"http","scheme":"bearer","bearerFormat":"Terra data token","description":"A data-plane token with vantage:read for reads or vantage:write for writes. Requires dev-id. Write includes read; data:read alone does not grant Vantage. Available after the API credential validator is configured."}},"parameters":{"ApiVersion":{"name":"X-Terra-Version","in":"header","required":false,"description":"The API version to serve this request in. Defaults to your account's pinned version; an account is pinned to the latest version on its first request.","schema":{"type":"string","enum":["2026-09-27","2026-10-01","2026-10-15"]}},"DryRun":{"name":"dry_run","in":"query","required":false,"description":"When true, run every check and return what the operation would do, with 200, without doing it.","schema":{"type":"boolean","default":false}}},"schemas":{"ResendWebhookEventRequest":{"type":"object","required":["event_id"],"properties":{"event_id":{"type":"string","description":"The `event_id` of the delivered webhook (also its `X-Terra-Trace-Id`)."},"event_type":{"type":"string","description":"Optional; when set it must match the stored event's type."}}},"ResendWebhookEventDryRunResponse":{"type":"object","required":["dry_run","would","preview"],"properties":{"dry_run":{"type":"boolean","const":true},"would":{"type":"string","const":"resend"},"preview":{"$ref":"#/components/schemas/ResendWebhookEventResponse"}}},"ResendWebhookEventResponse":{"type":"object","required":["event_id","event_type","dispatched"],"properties":{"event_id":{"type":"string"},"event_type":{"type":"string"},"dispatched":{"type":"boolean","description":"True when the event was queued; false in a dry-run preview."}}},"ProblemDetail":{"description":"Standard error response format following RFC 7807 specification","type":"object","properties":{"detail":{"type":"string"},"instance":{"type":"string"},"status":{"type":"integer"},"title":{"type":"string"},"type":{"type":"string"}}},"WebhookEventProblem":{"description":"RFC 7807 problem from the webhook-events operations, with a code to branch on and a remediation to show. (Flat rather than an allOf over ProblemDetail: the conformance gate seals declared objects.)","type":"object","required":["code","detail","status","title","type"],"properties":{"detail":{"type":"string"},"instance":{"type":"string"},"status":{"type":"integer"},"title":{"type":"string"},"type":{"type":"string"},"code":{"type":"string","enum":["not_found","event_not_resendable","webhook_url_missing"]},"remediation":{"type":"string"}}}},"headers":{"ApiVersion":{"description":"The API version this response was rendered in.","schema":{"type":"string","enum":["2026-09-27","2026-10-01","2026-10-15"]}},"RequestId":{"description":"A UUID generated for this request, unique per request and never taken from the caller. Quote it to support to find the request.","schema":{"type":"string","format":"uuid"}}}},"paths":{"/api/v1/webhook-events/resend":{"post":{"operationId":"resendWebhookEvent","summary":"Resend a webhook event","tags":["webhooks"],"description":"Sends a previously delivered event again, to your current webhook URL, signed with your current signing secret and rendered in your pinned version. The payload is rebuilt from the stored event, so it carries the status the event originally announced, not the order's current status, and a fresh `timestamp`. The event keeps its `event_id`: an endpoint that dedupes on it treats the resend as the event it already processed. Delivery is asynchronous, so 202 means the event was queued, not that it arrived; find the outcome with `GET /api/v1/webhook-deliveries`, where the resend replaces the event's earlier outcome. A delivered event can be resent for 30 days; an older one, or one delivered before Vantage began keeping delivered events, returns 404 with code `event_not_resendable`. Pass `dry_run=true` to check that an event can be resent without sending it.","parameters":[{"$ref":"#/components/parameters/ApiVersion"},{"$ref":"#/components/parameters/DryRun"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResendWebhookEventRequest"}}}},"responses":{"200":{"headers":{"X-Terra-Version":{"$ref":"#/components/headers/ApiVersion"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"description":"`dry_run=true`: the event can be resent; nothing was queued.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResendWebhookEventDryRunResponse"}}}},"202":{"headers":{"X-Terra-Version":{"$ref":"#/components/headers/ApiVersion"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"description":"Queued for redelivery.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResendWebhookEventResponse"}}}},"400":{"headers":{"X-Terra-Version":{"$ref":"#/components/headers/ApiVersion"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"description":"Malformed body, an event_id that is not an event id, an event_type that does not match the stored event, or a dry_run that is not a boolean. An unsupported `X-Terra-Version` also returns 400 with code `invalid_api_version`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"401":{"headers":{"X-Terra-Version":{"$ref":"#/components/headers/ApiVersion"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"404":{"headers":{"X-Terra-Version":{"$ref":"#/components/headers/ApiVersion"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"description":"No event with that event_id was sent to you (code `not_found`), or it cannot be resent (code `event_not_resendable`); `remediation` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEventProblem"}}}},"422":{"headers":{"X-Terra-Version":{"$ref":"#/components/headers/ApiVersion"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"description":"No webhook URL is configured (code `webhook_url_missing`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEventProblem"}}}},"500":{"headers":{"X-Terra-Version":{"$ref":"#/components/headers/ApiVersion"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"503":{"headers":{"X-Terra-Version":{"$ref":"#/components/headers/ApiVersion"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"description":"Current authorization is unavailable; retry after the dependency recovers.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}}}}}}}
```

## Send a test webhook event

> Sends a generated event of one webhook type to your current webhook URL, through the same delivery as real events: signed with \`X-Terra-Signature\`, rendered in your pinned version, and retried the same way. A generated event carries \`"test": true\` and describes no real order: every order, order item, variant and test-taker id in it is \`"0"\`, and the test taker is a placeholder. It changes no order or result. Selected product enforcement delivers it only while the environment has a current Vantage grant. The response carries the latest-shape payload; the delivery is stamped with its own \`timestamp\` when sent. Pass \`dry\_run=true\` to get the payload without sending it.

```json
{"openapi":"3.1.0","info":{"title":"Terra Vantage API","version":"2026.10.22"},"servers":[{"url":"https://vantage.tryterra.co"}],"security":[{"BasicAuth":[]},{"DataBearer":[],"DevIDHeader":[]}],"components":{"securitySchemes":{"BasicAuth":{"type":"http","scheme":"basic","description":"Authenticate with your Terra API credentials: username = your Terra\n`dev-id`, password = your Terra API key — the same credentials used for\nevery other Terra API. The header pair `dev-id` + `x-api-key` is also\naccepted. Suspended credentials receive 403; accounts without the\nVantage entitlement receive 403 once entitlement enforcement is\nenabled. Once the credential validator is configured, backfilled keys\nalso honor current token expiry, revocation, audience and Vantage scopes.\nInvalid tokens receive 401, insufficient scopes 403 and an unavailable\nvalidator 503. These checks remain active when product policy is off.\n"},"DataBearer":{"type":"http","scheme":"bearer","bearerFormat":"Terra data token","description":"A data-plane token with vantage:read for reads or vantage:write for writes. Requires dev-id. Write includes read; data:read alone does not grant Vantage. Available after the API credential validator is configured."}},"parameters":{"ApiVersion":{"name":"X-Terra-Version","in":"header","required":false,"description":"The API version to serve this request in. Defaults to your account's pinned version; an account is pinned to the latest version on its first request.","schema":{"type":"string","enum":["2026-09-27","2026-10-01","2026-10-15"]}},"DryRun":{"name":"dry_run","in":"query","required":false,"description":"When true, run every check and return what the operation would do, with 200, without doing it.","schema":{"type":"boolean","default":false}}},"schemas":{"GenerateWebhookEventRequest":{"type":"object","required":["event_type"],"properties":{"event_type":{"type":"string","enum":["order.status_changed","order_item.results_status_change"]},"status":{"type":"string","description":"The `data.status` (for order.status_changed) or `data.results_status` (for order_item.results_status_change) the event carries, from that webhook's enum. Defaults to `order.delivery_fulfilled` and `results.results_ready`."}}},"GenerateWebhookEventDryRunResponse":{"type":"object","required":["dry_run","would","preview"],"properties":{"dry_run":{"type":"boolean","const":true},"would":{"type":"string","const":"dispatch"},"preview":{"$ref":"#/components/schemas/GenerateWebhookEventResponse"}}},"GenerateWebhookEventResponse":{"type":"object","required":["event_id","event_type","dispatched","payload"],"properties":{"event_id":{"type":"string"},"event_type":{"type":"string"},"dispatched":{"type":"boolean","description":"True when the event was queued; false in a dry-run preview."},"payload":{"description":"The latest-shape webhook body.","oneOf":[{"$ref":"#/components/schemas/OrderStatusChangedEvent"},{"$ref":"#/components/schemas/OrderItemResultsStatusChangedEvent"}]}}},"OrderStatusChangedEvent":{"type":"object","required":["event_type","event_id","timestamp","data"],"properties":{"event_type":{"type":"string","const":"order.status_changed"},"event_id":{"type":"string","description":"Unique per event (64-bit id serialized as a string) — deduplicate on this. Equals the X-Terra-Trace-Id header."},"timestamp":{"type":"integer","description":"Unix seconds when the event was sent."},"test":{"type":"boolean","const":true,"description":"Present only on events from `POST /api/v1/webhook-events/generate`, which describe no real order."},"data":{"type":"object","required":["order_id","status"],"properties":{"order_id":{"type":"string"},"status":{"type":"string","enum":["order.payment_processing","order.payment_complete","order.payment_failed","order.processing","order.delayed","order.cancelled","order.completed","order.delivery_fulfilled"]},"tracking_number":{"type":"string","description":"Present once delivery details are available."},"supplier_item_id":{"type":"string","description":"Sandbox payloads only — the supplier-side kit ID."}}}}},"OrderItemResultsStatusChangedEvent":{"type":"object","required":["event_type","event_id","timestamp","data"],"properties":{"event_type":{"type":"string","const":"order_item.results_status_change"},"event_id":{"type":"string","description":"Unique per event (64-bit id serialized as a string) — deduplicate on this. Equals the X-Terra-Trace-Id header."},"timestamp":{"type":"integer","description":"Unix seconds when the event was sent."},"test":{"type":"boolean","const":true,"description":"Present only on events from `POST /api/v1/webhook-events/generate`, which describe no real order."},"data":{"type":"object","required":["order_id","order_item_id","variant_id","results_status","test_taker"],"properties":{"order_id":{"type":"string"},"order_item_id":{"type":"string"},"variant_id":{"type":"string"},"results_status":{"type":"string","enum":["results.kit_activated","results.sample_processing_in_lab","results.sample_rejected","results.partial_results_ready","results.results_ready","results.lab_processing_error","results.escalation_raised","results.genome_processing","results.sample_reanalysis","results.reanalysed"],"description":"Non-exhaustive: new statuses may be added without a new API version, so treat an unknown value as an in-progress state. `genome_processing`, `sample_reanalysis` and `reanalysed` are DNA-only."},"test_taker":{"type":"object","required":["test_taker_id","first_name","last_name","email","phone_number"],"properties":{"test_taker_id":{"type":"string"},"first_name":{"type":"string"},"last_name":{"type":"string"},"email":{"type":"string"},"phone_number":{"type":"string","description":"E.164 phone number"}}},"escalation_level":{"type":"string","description":"Present on escalation_raised.","enum":["not_escalated","very_low","low","medium","high","very_high"]},"acknowledgment_due_by":{"type":"string","description":"Present on escalation_raised — ISO 8601 deadline."},"failure_cause":{"type":"string","description":"Present on sample_rejected."},"result_version":{"type":"integer","format":"int32","minimum":1,"description":"Present on results_ready and reanalysed: the version of the result now served, 1 for the first result and bumped by every re-analysis. Fetch the result again when it changes."},"amended":{"type":"boolean","const":true,"description":"Present only on results_ready for a later corrected release of a result already delivered; fetch the result again."}}}}},"ProblemDetail":{"description":"Standard error response format following RFC 7807 specification","type":"object","properties":{"detail":{"type":"string"},"instance":{"type":"string"},"status":{"type":"integer"},"title":{"type":"string"},"type":{"type":"string"}}},"WebhookEventProblem":{"description":"RFC 7807 problem from the webhook-events operations, with a code to branch on and a remediation to show. (Flat rather than an allOf over ProblemDetail: the conformance gate seals declared objects.)","type":"object","required":["code","detail","status","title","type"],"properties":{"detail":{"type":"string"},"instance":{"type":"string"},"status":{"type":"integer"},"title":{"type":"string"},"type":{"type":"string"},"code":{"type":"string","enum":["not_found","event_not_resendable","webhook_url_missing"]},"remediation":{"type":"string"}}}},"headers":{"ApiVersion":{"description":"The API version this response was rendered in.","schema":{"type":"string","enum":["2026-09-27","2026-10-01","2026-10-15"]}},"RequestId":{"description":"A UUID generated for this request, unique per request and never taken from the caller. Quote it to support to find the request.","schema":{"type":"string","format":"uuid"}}}},"paths":{"/api/v1/webhook-events/generate":{"post":{"operationId":"generateWebhookEvent","summary":"Send a test webhook event","tags":["webhooks"],"description":"Sends a generated event of one webhook type to your current webhook URL, through the same delivery as real events: signed with `X-Terra-Signature`, rendered in your pinned version, and retried the same way. A generated event carries `\"test\": true` and describes no real order: every order, order item, variant and test-taker id in it is `\"0\"`, and the test taker is a placeholder. It changes no order or result. Selected product enforcement delivers it only while the environment has a current Vantage grant. The response carries the latest-shape payload; the delivery is stamped with its own `timestamp` when sent. Pass `dry_run=true` to get the payload without sending it.","parameters":[{"$ref":"#/components/parameters/ApiVersion"},{"$ref":"#/components/parameters/DryRun"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateWebhookEventRequest"}}}},"responses":{"200":{"headers":{"X-Terra-Version":{"$ref":"#/components/headers/ApiVersion"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"description":"`dry_run=true`: the payload that would be sent; nothing was queued.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateWebhookEventDryRunResponse"}}}},"202":{"headers":{"X-Terra-Version":{"$ref":"#/components/headers/ApiVersion"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"description":"Queued for delivery.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateWebhookEventResponse"}}}},"400":{"headers":{"X-Terra-Version":{"$ref":"#/components/headers/ApiVersion"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"description":"Malformed body, an unknown event_type, a status that event type does not carry, or a dry_run that is not a boolean. An unsupported `X-Terra-Version` also returns 400 with code `invalid_api_version`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"401":{"headers":{"X-Terra-Version":{"$ref":"#/components/headers/ApiVersion"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"description":"Missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"422":{"headers":{"X-Terra-Version":{"$ref":"#/components/headers/ApiVersion"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"description":"No webhook URL is configured (code `webhook_url_missing`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEventProblem"}}}},"500":{"headers":{"X-Terra-Version":{"$ref":"#/components/headers/ApiVersion"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"503":{"headers":{"X-Terra-Version":{"$ref":"#/components/headers/ApiVersion"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"description":"Current authorization is unavailable; retry after the dependency recovers.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}}}}}}}
```
