Long work runs in the background.
Live rates, batch rates and URL-based reconciliation answer 202 with a Job object, the same one GET /v2/jobs/{job_id} returns. Poll it using estimated_seconds to space the calls, or register a webhook and wait. Jobs are kept for 7 days. Both are free.
A finished job carries its result in result. Batch results can be large, so GET /v2/jobs/{job_id}/result streams them as NDJSON, one line per hotel and stay (vistalink jobs result); result_url is a download link when one exists.
/v2/jobs/{job_id}/v2/jobs/{job_id}/result/v2/jobs/v2/jobs/{job_id}/cancel/v2/webhooks/v2/webhooks/v2/webhooks/{webhook_id}/v2/webhooks/{webhook_id}/deliveriesGet a job
/v2/jobs/{job_id}Synchronous and free; any key with at least one scope can read its own jobs. Poll until status is succeeded, failed or canceled; jobs expire 7 days after creation.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
job_idREQUIRED | path | string | The job_id from a 202 response. |
Example
curl 'https://api.vistalink.com/v2/jobs/job_K8s2Lq9Wx4Np' \
-H "Authorization: Bearer $VISTALINK_API_KEY"{
"created_at": "2026-10-07T09:30:00Z",
"estimated_seconds": 25,
"job_id": "job_K8s2Lq9Wx4Np",
"kind": "rates.live",
"progress": {
"done": 0,
"total": 1
},
"status": "running",
"updated_at": "2026-10-07T09:30:00Z"
}Response 200
Successful Response
| Field | Type | Description |
|---|---|---|
request_id | string | null | Id of the request that returned this job; null in lists and webhook payloads. |
job_idREQUIRED | string | Job id, job_ plus 12 letters and digits. |
kindREQUIRED | string | rates.live (from POST /v2/rates or rates=refresh), rates.batch or rooms.reconcile. One of rates.live, rates.batch, rooms.reconcile. |
statusREQUIRED | string | queued, running, then succeeded, failed or canceled. One of queued, running, succeeded, failed, canceled. |
progress | object | Units of work done out of the total. |
progress.done | integer | Units of work finished. Default 0. |
progress.total | integer | Units of work in the job: hotels for rates.live, hotel and stay pairs for rates.batch, 1 for rooms.reconcile. Default 1. |
created_atREQUIRED | string (date-time) | When the job was created, RFC 3339 UTC; jobs expire 7 days later. |
updated_atREQUIRED | string (date-time) | When the job last changed, RFC 3339 UTC. |
estimated_seconds | integer | null | Rough time the job was expected to take when it was created, in seconds. |
result | object | null | The outcome once succeeded: the POST /v2/rates response for rates.live, {summary, lines, format, warnings} for rates.batch, the reconcile result for rooms.reconcile. Null otherwise. |
result_url | string (uri) | null | rates.batch only: where to download the NDJSON result, a signed link or GET /v2/jobs/{job_id}/result; null for other kinds and until the job succeeds. |
error | object | null | Why the job failed; null unless status is failed. |
error.codeREQUIRED | string | Error code, one of the API error codes, usually upstream_error, unavailable (the job stopped and its held credit was returned) or bad_request. |
error.messageREQUIRED | string | Explanation in English. |
billing | Billing | null | What a billed call cost. Present on every billed response; billing is null on free ones. |
Errors
| Status | Code | When |
|---|---|---|
| 400 | bad_request | Validation errors carry details.fields[]. |
| 401 | unauthorized | No key, or the key is unknown or revoked. |
| 403 | forbidden | Missing scope. |
| 404 | not_found | The hotel, offer, booking, job or webhook doesn't exist for this key. |
| 429 | rate_limited | Over the per-minute limit for the tier. Wait Retry-After seconds. |
| 503 | unavailable | We're temporarily unable to serve the call. Retry with backoff. |
Download a job's result
/v2/jobs/{job_id}/resultSynchronous and free; any key with at least one scope. The NDJSON result of a rates.batch job, one line per hotel and stay, served here when the job's result_url points at this path.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
job_idREQUIRED | path | string | The job_id of a rates.batch job. |
Example
curl 'https://api.vistalink.com/v2/jobs/job_K8s2Lq9Wx4Np/result' \
-H "Authorization: Bearer $VISTALINK_API_KEY"{"input":0,"hotel_id":"43d0be71-5f0e-4f1e-9a51-2b7b3c4d5e6f","stay":{"check_in":"2026-11-14","check_out":"2026-11-16"},"status":"observed","channel":"direct","engine":"SYNXIS","observed_at":"2026-10-07T09:30:00Z","rooms":[]}
{"input":1,"hotel_id":"43d0be71-5f0e-4f1e-9a51-2b7b3c4d5e6f","stay":{"check_in":"2026-11-21","check_out":"2026-11-23"},"status":"unknown","channel":"direct","rooms":[]}Response 200
NDJSON lines
Errors
| Status | Code | When |
|---|---|---|
| 400 | bad_request | Validation errors carry details.fields[]. |
| 401 | unauthorized | No key, or the key is unknown or revoked. |
| 403 | forbidden | Missing scope. |
| 404 | not_found | The hotel, offer, booking, job or webhook doesn't exist for this key. |
| 429 | rate_limited | Over the per-minute limit for the tier. Wait Retry-After seconds. |
| 503 | unavailable | We're temporarily unable to serve the call. Retry with backoff. |
List jobs
/v2/jobsSynchronous and free; any key with at least one scope. Newest first; pass next_cursor back as cursor for the next page.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
kind | query | string | Only jobs of this kind. One of rates.live, rates.batch, rooms.reconcile. |
status | query | string | Only jobs in this status. One of queued, running, succeeded, failed, canceled. |
cursor | query | object | The next_cursor of the previous page; omit for the first page. |
limit | query | integer | Jobs per page. |
Example
curl 'https://api.vistalink.com/v2/jobs?cursor=MjAyNi0xMC0wN1QwOTozMDowMCswMDowMHxqb2JfSzhzMkxxOVd4NE5w' \
-H "Authorization: Bearer $VISTALINK_API_KEY"{
"jobs": [
{
"billing": {
"amount": "0.04",
"currency": "USD",
"sku": "rates.direct.live",
"units": 1
},
"created_at": "2026-10-07T09:30:00Z",
"estimated_seconds": 25,
"job_id": "job_K8s2Lq9Wx4Np",
"kind": "rates.live",
"progress": {
"done": 1,
"total": 1
},
"result": {
"billing": {
"amount": "0.002",
"currency": "USD",
"sku": "rates.direct.cached",
"units": 1
},
"request_id": "req_8f2c1a9b3d4e5f60",
"results": [
{
"channel": "direct",
"engine": "SYNXIS",
"hotel_id": "43d0be71-5f3c-4c8e-9a52-1d7e2b6f0a19",
"input": 0,
"observed_at": "2026-10-07T09:30:00Z",
"rooms": [
{
"listing_key": "engine_room:1234567",
"name": "Superior Room",
"offers": [
{
"availability": "available",
"board": "breakfast",
"channel": "direct",
"currency": "EUR",
"engine": "SYNXIS",
"excluded_charges": [],
"free_cancellation": false,
"includes_taxes": true,
"listing_key": "engine_room:1234567",
"nightly_avg": "481.00",
"observed_at": "2026-10-07T09:30:00Z",
"occupancy": {
"adults": 2,
"children_ages": []
},
"offer_id": "off_t3c8e1a9b4f2d7e6a5c0b",
"rate_name": "Non-refundable, breakfast included",
"room_id": "5b1e2f3a-0c4d-4e5f-8a9b-1c2d3e4f5a6b",
"room_name": "Superior Room",
"total": "962.00",
"url": "https://umilta36.com/book?checkin=2026-11-14&checkout=2026-11-16"
}
],
"room_id": "5b1e2f3a-0c4d-4e5f-8a9b-1c2d3e4f5a6b"
}
],
"status": "observed"
}
],
"summary": {
"observed": 1,
"stale": 0,
"unknown": 0,
"unresolved": 0,
"unsupported": 0
}
},
"status": "succeeded",
"updated_at": "2026-10-07T09:30:00Z"
}
],
"request_id": "req_8f2c1a9b3d4e5f60"
}Response 200
Successful Response
| Field | Type | Description |
|---|---|---|
request_idREQUIRED | string | Opaque id of this request, also sent as the X-Request-Id header; quote it when contacting support. |
billing | Billing | null | What a billed call cost. Present on every billed response; billing is null on free ones. |
jobsREQUIRED | Job[] | Jobs on this page, newest first. |
next_cursor | string | null | Pass as cursor for the next page; null on the last page. |
Errors
| Status | Code | When |
|---|---|---|
| 400 | bad_request | Validation errors carry details.fields[]. |
| 401 | unauthorized | No key, or the key is unknown or revoked. |
| 403 | forbidden | Missing scope. |
| 429 | rate_limited | Over the per-minute limit for the tier. Wait Retry-After seconds. |
| 503 | unavailable | We're temporarily unable to serve the call. Retry with backoff. |
Cancel a job
/v2/jobs/{job_id}/cancelSynchronous and free; needs the scope of the job's kind (rates for rates.*, reconcile for rooms.reconcile). Only queued or running jobs can be canceled (409 otherwise), and the credit held for the job is released.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
job_idREQUIRED | path | string | The job_id to cancel. |
Example
curl -X POST 'https://api.vistalink.com/v2/jobs/job_K8s2Lq9Wx4Np/cancel' \
-H "Authorization: Bearer $VISTALINK_API_KEY"{
"created_at": "2026-10-07T09:30:00Z",
"estimated_seconds": 25,
"job_id": "job_K8s2Lq9Wx4Np",
"kind": "rates.live",
"progress": {
"done": 0,
"total": 1
},
"status": "running",
"updated_at": "2026-10-07T09:30:00Z"
}Response 200
Successful Response
| Field | Type | Description |
|---|---|---|
request_id | string | null | Id of the request that returned this job; null in lists and webhook payloads. |
job_idREQUIRED | string | Job id, job_ plus 12 letters and digits. |
kindREQUIRED | string | rates.live (from POST /v2/rates or rates=refresh), rates.batch or rooms.reconcile. One of rates.live, rates.batch, rooms.reconcile. |
statusREQUIRED | string | queued, running, then succeeded, failed or canceled. One of queued, running, succeeded, failed, canceled. |
progress | object | Units of work done out of the total. |
progress.done | integer | Units of work finished. Default 0. |
progress.total | integer | Units of work in the job: hotels for rates.live, hotel and stay pairs for rates.batch, 1 for rooms.reconcile. Default 1. |
created_atREQUIRED | string (date-time) | When the job was created, RFC 3339 UTC; jobs expire 7 days later. |
updated_atREQUIRED | string (date-time) | When the job last changed, RFC 3339 UTC. |
estimated_seconds | integer | null | Rough time the job was expected to take when it was created, in seconds. |
result | object | null | The outcome once succeeded: the POST /v2/rates response for rates.live, {summary, lines, format, warnings} for rates.batch, the reconcile result for rooms.reconcile. Null otherwise. |
result_url | string (uri) | null | rates.batch only: where to download the NDJSON result, a signed link or GET /v2/jobs/{job_id}/result; null for other kinds and until the job succeeds. |
error | object | null | Why the job failed; null unless status is failed. |
error.codeREQUIRED | string | Error code, one of the API error codes, usually upstream_error, unavailable (the job stopped and its held credit was returned) or bad_request. |
error.messageREQUIRED | string | Explanation in English. |
billing | Billing | null | What a billed call cost. Present on every billed response; billing is null on free ones. |
Errors
| Status | Code | When |
|---|---|---|
| 400 | bad_request | Validation errors carry details.fields[]. |
| 401 | unauthorized | No key, or the key is unknown or revoked. |
| 403 | forbidden | Missing scope. |
| 404 | not_found | The hotel, offer, booking, job or webhook doesn't exist for this key. |
| 409 | conflict | The Idempotency-Key was reused with a different body, or the resource is in a state that forbids the change. |
| 429 | rate_limited | Over the per-minute limit for the tier. Wait Retry-After seconds. |
| 503 | unavailable | We're temporarily unable to serve the call. Retry with backoff. |
List webhooks
/v2/webhooksSynchronous and free; any key with at least one scope. The tenant's active webhooks, newest first; secrets are never returned again.
Example
curl 'https://api.vistalink.com/v2/webhooks' \
-H "Authorization: Bearer $VISTALINK_API_KEY"{
"request_id": "req_8f2c1a9b3d4e5f60",
"webhooks": [
{
"created_at": "2026-10-07T09:30:00Z",
"events": [
"rates.completed",
"booking.updated"
],
"is_active": true,
"url": "https://partner.example/hooks/vistalink",
"webhook_id": "wh_N4c7Bz1Qk2Vt"
}
]
}Response 200
Successful Response
| Field | Type | Description |
|---|---|---|
request_idREQUIRED | string | Opaque id of this request, also sent as the X-Request-Id header; quote it when contacting support. |
billing | Billing | null | What a billed call cost. Present on every billed response; billing is null on free ones. |
webhooksREQUIRED | Webhook[] | Active webhooks, newest first. |
webhooks[].webhook_idREQUIRED | string | Webhook id, wh_ plus 12 letters and digits. |
webhooks[].urlREQUIRED | string (uri) | The https endpoint deliveries are POSTed to. |
webhooks[].eventsREQUIRED | string[] | Events delivered to it. One of rates.completed, rates.batch.completed, rooms.reconcile.completed, booking.updated. |
webhooks[].created_atREQUIRED | string (date-time) | When the webhook was created, RFC 3339 UTC. |
webhooks[].is_active | boolean | Always true; deleted webhooks are not listed. Default true. |
Errors
| Status | Code | When |
|---|---|---|
| 400 | bad_request | Validation errors carry details.fields[]. |
| 401 | unauthorized | No key, or the key is unknown or revoked. |
| 403 | forbidden | Missing scope. |
| 429 | rate_limited | Over the per-minute limit for the tier. Wait Retry-After seconds. |
| 503 | unavailable | We're temporarily unable to serve the call. Retry with backoff. |
Create a webhook
/v2/webhooksSynchronous and free; each event needs its family's scope (booking.updated needs booking, rates.* need rates, rooms.reconcile.completed needs reconcile). Deliveries are signed POSTs described under Webhooks, retried after 1m, 5m, 30m, 2h, 12h and 24h; the secret is returned only in this response.
Request body
| Field | Type | Description |
|---|---|---|
urlREQUIRED | string (uri) | The https endpoint deliveries are POSTed to; it must resolve to a public address. |
eventsREQUIRED | string[] | Events to deliver, each needing its family's scope on the key: booking.updated (booking), rates.completed and rates.batch.completed (rates), rooms.reconcile.completed (reconcile). At most 4. One of rates.completed, rates.batch.completed, rooms.reconcile.completed, booking.updated. |
secret | string | null | Your own signing secret, 24 to 128 characters; one is generated when omitted. |
Example
curl -X POST 'https://api.vistalink.com/v2/webhooks' \
-H "Authorization: Bearer $VISTALINK_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://partner.example/hooks/vistalink",
"events": [
"rates.completed",
"booking.updated"
]
}'{
"created_at": "2026-10-07T09:30:00Z",
"events": [
"rates.completed",
"booking.updated"
],
"request_id": "req_8f2c1a9b3d4e5f60",
"secret": "whsec_3f9a0c2e7b5d4a1f8e6c9b2a7d4f1e0c",
"url": "https://partner.example/hooks/vistalink",
"webhook_id": "wh_N4c7Bz1Qk2Vt"
}Response 201
Successful Response
| Field | Type | Description |
|---|---|---|
request_idREQUIRED | string | Opaque id of this request, also sent as the X-Request-Id header; quote it when contacting support. |
billing | Billing | null | What a billed call cost. Present on every billed response; billing is null on free ones. |
webhook_idREQUIRED | string | Webhook id, wh_ plus 12 letters and digits. |
secretREQUIRED | string | Signing secret for X-VistaLink-Signature, whsec_ plus hex when generated; shown only in this response. |
urlREQUIRED | string (uri) | The https endpoint deliveries are POSTed to. |
eventsREQUIRED | string[] | Events delivered to it. One of rates.completed, rates.batch.completed, rooms.reconcile.completed, booking.updated. |
created_atREQUIRED | string (date-time) | When the webhook was created, RFC 3339 UTC. |
Errors
| Status | Code | When |
|---|---|---|
| 400 | bad_request | Validation errors carry details.fields[]. |
| 401 | unauthorized | No key, or the key is unknown or revoked. |
| 403 | forbidden | Missing scope. |
| 409 | conflict | Too many webhooks. |
| 429 | rate_limited | Over the per-minute limit for the tier. Wait Retry-After seconds. |
| 503 | unavailable | We're temporarily unable to serve the call. Retry with backoff. |
Delete a webhook
/v2/webhooks/{webhook_id}Synchronous and free; needs the scope of every event the webhook subscribes to. Answers 204 with no body, and deliveries still pending for it are dropped.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
webhook_idREQUIRED | path | string | The webhook_id returned when the webhook was created. |
Example
curl -X DELETE 'https://api.vistalink.com/v2/webhooks/wh_N4c7Bz1Qk2Vt' \
-H "Authorization: Bearer $VISTALINK_API_KEY"// 204 No ContentResponse 204
Successful Response
Errors
| Status | Code | When |
|---|---|---|
| 400 | bad_request | Validation errors carry details.fields[]. |
| 401 | unauthorized | No key, or the key is unknown or revoked. |
| 403 | forbidden | Missing scope. |
| 404 | not_found | The hotel, offer, booking, job or webhook doesn't exist for this key. |
| 429 | rate_limited | Over the per-minute limit for the tier. Wait Retry-After seconds. |
| 503 | unavailable | We're temporarily unable to serve the call. Retry with backoff. |
List deliveries
/v2/webhooks/{webhook_id}/deliveriesSynchronous and free; any key with at least one scope. Delivery attempts for one webhook, newest first, with the last HTTP status and error of each.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
webhook_idREQUIRED | path | string | The webhook_id whose deliveries to list. |
cursor | query | object | The next_cursor of the previous page; omit for the first page. |
limit | query | integer | Deliveries per page, newest first. |
Example
curl 'https://api.vistalink.com/v2/webhooks/wh_N4c7Bz1Qk2Vt/deliveries?cursor=MjAyNi0xMC0wN1QwOTozMDowMCswMDowMHx3aGRfUjJ0OE14NVFwMUx6' \
-H "Authorization: Bearer $VISTALINK_API_KEY"{
"deliveries": [
{
"attempts": 1,
"created_at": "2026-10-07T09:30:00Z",
"delivered_at": "2026-10-07T09:30:01Z",
"delivery_id": "whd_R2t8Mx5Qp1Lz",
"event": "booking.updated",
"event_id": "evt_9Hd2Kq7Lm4Xp1Zr8",
"last_status_code": 200,
"status": "delivered"
}
],
"request_id": "req_8f2c1a9b3d4e5f60"
}Response 200
Successful Response
| Field | Type | Description |
|---|---|---|
request_idREQUIRED | string | Opaque id of this request, also sent as the X-Request-Id header; quote it when contacting support. |
billing | Billing | null | What a billed call cost. Present on every billed response; billing is null on free ones. |
deliveriesREQUIRED | WebhookDelivery[] | Deliveries on this page, newest first. |
deliveries[].delivery_idREQUIRED | string | Delivery id, whd_ plus 12 letters and digits. |
deliveries[].eventREQUIRED | string | The event delivered. One of rates.completed, rates.batch.completed, rooms.reconcile.completed, booking.updated. |
deliveries[].event_idREQUIRED | string | Id of the event, evt_ plus 16 letters and digits, the same for every webhook that receives it and sent as id in the payload. |
deliveries[].statusREQUIRED | string | pending (waiting for its next attempt), delivered (your endpoint answered 2xx) or failed (all attempts used, or the webhook was deleted). One of pending, delivered, failed. |
deliveries[].attemptsREQUIRED | integer | Attempts made so far, at most 7. |
deliveries[].last_status_code | integer | null | HTTP status your endpoint gave on the last attempt; null when it could not be reached. |
deliveries[].last_error | string | null | Why the last attempt failed, for example http_500 or webhook_deleted; null after success. |
deliveries[].next_attempt_at | string (date-time) | null | When the next attempt is due, RFC 3339 UTC; null once delivered or failed. |
deliveries[].created_atREQUIRED | string (date-time) | When the event was queued for this webhook, RFC 3339 UTC. |
deliveries[].delivered_at | string (date-time) | null | When your endpoint accepted it, RFC 3339 UTC; null until then. |
next_cursor | string | null | Pass as cursor for the next page; null on the last page. |
Errors
| Status | Code | When |
|---|---|---|
| 400 | bad_request | Validation errors carry details.fields[]. |
| 401 | unauthorized | No key, or the key is unknown or revoked. |
| 403 | forbidden | Missing scope. |
| 404 | not_found | The hotel, offer, booking, job or webhook doesn't exist for this key. |
| 429 | rate_limited | Over the per-minute limit for the tier. Wait Retry-After seconds. |
| 503 | unavailable | We're temporarily unable to serve the call. Retry with backoff. |
Webhook events
Every delivery is a POST with the same envelope. See the WebhookEventPayload object below.
| Event | When |
|---|---|
rates.completed | A rates.live job finished. data is the Job. |
rates.batch.completed | A rates.batch job finished. data is the Job. |
rooms.reconcile.completed | A rooms.reconcile job finished. data is the Job. |
booking.updated | Every change to a booking's status or payment.status. data is the Booking. |
WebhookEventPayload
The JSON body POSTed to your webhook URL, signed in X-VistaLink-Signature.
| Field | Type | Description |
|---|---|---|
eventREQUIRED | string | The event: rates.completed, rates.batch.completed or rooms.reconcile.completed when a job finishes (succeeded or failed), booking.updated when a booking's status or payment status changes. One of rates.completed, rates.batch.completed, rooms.reconcile.completed, booking.updated. |
idREQUIRED | string | Event id, evt_ plus 16 characters; use it to discard repeats, since a retried delivery carries the same id. |
created_atREQUIRED | string (date-time) | When the event happened, RFC 3339 UTC. |
dataREQUIRED | object | The Job for job events, the Booking for booking.updated. |
Verifying signatures
Each delivery carries X-VistaLink-Signature: t=<unix>,v1=<hex>. v1 is the HMAC-SHA256 of <t>.<raw body> with your secret. Compare in constant time and reject old timestamps.
Any 2xx acknowledges a delivery. Anything else is retried after 1m, 5m, 30m, 2h, 12h, 24h. The event id is stable across retries, so dedupe on it.
Creating a webhook answers 201 with the whole webhook and its secret, shown only then. Webhooks carry is_active. List a webhook's deliveries to see each one's status (pending, delivered or failed), its attempts, your endpoint's last_status_code and last_error, and next_attempt_at while it's still pending.
import { createHmac, timingSafeEqual } from "node:crypto";
// header: "t=1791364462,v1=5f2c…"; body: the raw request body string
export function verify(header: string, body: string, secret: string): boolean {
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
const expected = createHmac("sha256", secret)
.update(`${parts.t}.${body}`)
.digest("hex");
const a = Buffer.from(expected);
const b = Buffer.from(parts.v1 ?? "");
const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300;
return fresh && a.length === b.length && timingSafeEqual(a, b);
}Shared objects.
Objects that appear in more than one place. Billing is on the Billing page and ErrorBody under Errors & limits.
Job
Background work started by a 202 response. Also the data of the *.completed webhooks.
| Field | Type | Description |
|---|---|---|
request_id | string | null | Id of the request that returned this job; null in lists and webhook payloads. |
job_idREQUIRED | string | Job id, job_ plus 12 letters and digits. |
kindREQUIRED | string | rates.live (from POST /v2/rates or rates=refresh), rates.batch or rooms.reconcile. One of rates.live, rates.batch, rooms.reconcile. |
statusREQUIRED | string | queued, running, then succeeded, failed or canceled. One of queued, running, succeeded, failed, canceled. |
progress | object | Units of work done out of the total. |
progress.done | integer | Units of work finished. Default 0. |
progress.total | integer | Units of work in the job: hotels for rates.live, hotel and stay pairs for rates.batch, 1 for rooms.reconcile. Default 1. |
created_atREQUIRED | string (date-time) | When the job was created, RFC 3339 UTC; jobs expire 7 days later. |
updated_atREQUIRED | string (date-time) | When the job last changed, RFC 3339 UTC. |
estimated_seconds | integer | null | Rough time the job was expected to take when it was created, in seconds. |
result | object | null | The outcome once succeeded: the POST /v2/rates response for rates.live, {summary, lines, format, warnings} for rates.batch, the reconcile result for rooms.reconcile. Null otherwise. |
result_url | string (uri) | null | rates.batch only: where to download the NDJSON result, a signed link or GET /v2/jobs/{job_id}/result; null for other kinds and until the job succeeds. |
error | object | null | Why the job failed; null unless status is failed. |
error.codeREQUIRED | string | Error code, one of the API error codes, usually upstream_error, unavailable (the job stopped and its held credit was returned) or bad_request. |
error.messageREQUIRED | string | Explanation in English. |
billing | Billing | null | What a billed call cost. Present on every billed response; billing is null on free ones. |