Skip to main content
The legacy Calls API (POST /v1/calls, scriptId) is replaced by the Requests API (POST /v1/requests, schemaId). Legacy endpoints sunset on October 1, 2026. The dialing capability is the same. What changes is the shape of the integration: a request replaces a call as the unit of work, a schema replaces a script, and results come back as schema-defined named fields instead of question-indexed answers. Several things you might assume are new already exist in the legacy API: the GET /v1/auth token exchange, structured errors, and definition listing. This guide calls those out so you don’t rebuild what you already have.
Two changes break silently if you miss them. The dialed number moves from a top-level to field into inputs.phoneNumber (see Submitting work), and the Requests API needs its own webhook URL, separate from your legacy one (see Configure the Request webhook URL). Neither returns an error when you get it wrong.

1. At a glance

The base host does not change: https://robodialer-service-api-9nc4t1p9.uc.gateway.dev. Sandbox and production continue to use separate credentials against that one host.

2. Authentication

Authentication is unchanged. Both APIs use the same GET /v1/auth exchange: trade your long-lived key and secret for a short-lived bearer token, then attach that token to every subsequent request.
Pass it as Authorization: Bearer <token>. Tokens are valid for one hour.
No change required if you already exchange credentials at GET /v1/auth and cache the token (up to about 55 minutes, refreshing before expiry). Keep your key and secret in a secret store: they are long-lived, the token is not. See Production vs Sandbox for which key to use when.

3. Submitting work

Rename scriptId to schemaId, post to /v1/requests, and move the dialed number into inputs.phoneNumber. internalId, internalTag, and webhookUrl carry over unchanged.

Legacy: POST /v1/calls

New: POST /v1/requests

There is no top-level to field on POST /v1/requests. The dialed number is an input: inputs.phoneNumber. A body that sends to at the top level is not rejected, because unknown top-level keys are ignored. Instead the request fails input validation for a missing phoneNumber, or, if your account has payer phone number lookup enabled, it silently dials a number you did not choose. to does still appear in the response, where it reports the number actually dialed.
Field mapping: scriptId to schemaId, to to inputs.phoneNumber, callId to requestId, batchId to requestBatchId. inputs, internalId, and internalTag carry through unchanged. webhookUrl remains an optional per-request override. The create response also always carries a payerLookup object describing how the payer and number were resolved. See Creating a Request for the full field list.

Batch submission

Rename the wrapper key calls to requests.
The batch response returns a per-item array, and an item may carry an error instead of a requestId, so check every element. Note two differences from legacy: the per-item error key is singular error (legacy used plural errors), and the aggregate status code is not simply 207. A batch returns 200 when every item succeeds, 500 if any item failed with a server error, 207 for a mix of successes and client errors, and 400 when every item failed with a client error. Treat 500 as “partial results may still be present” rather than “nothing was created”. See Batch.

4. Discovering inputs and schemas

You can enumerate your provisioned schemas programmatically. This is not new capability: the legacy API already exposes GET /v1/scripts, which returns your scripts as [{ "id", "type", "name", "createdAt" }]. GET /v1/schemas is its direct replacement. Map id to schemaId and type to requestType.
Use this to confirm where the dialed number belongs: phoneNumber appears in requiredInputs.fields, not as a separate top-level parameter. See Schemas.

5. Retrieving results

The largest data-model change is the results shape. Legacy answers were keyed by question index. New results are named fields defined by your schema, which removes the positional coupling.

Legacy: GET /v1/calls/

New: GET /v1/requests/

Replace any code that reads results["0"].answer or relies on resultsByAlias or highLevelOutputs with code that reads named fields from results (for example results.claimStatus). Use GET /v1/schemas/{schemaId}/required-inputs and your schema definition to know which field names to expect.
Two field-naming details worth knowing before you write parsers. data_completeness really is snake_case among camelCase siblings, and today it is either "minimum" or null. And to is omitted from the response entirely when SuperDial resolved the number on your behalf, so treat it as optional rather than always present. See Single-request vs list response shapes.

States and errors

Legacy was not binary either. Alongside success and failure it returned processing, plus cancelled and paused. The mapping is processing to PROCESSING, success to SUCCESS, failure to FAILURE. The genuinely new state to start handling is PARTIAL.
On PARTIAL, error is null. Read missingFields to find out what was not captured. Only FAILURE populates the error object, so code that branches on error being non-null will treat every partial result as a clean success.
Note also that PROCESSING is where anything non-terminal lands, including internal error and cancelled states. A request sitting in PROCESSING indefinitely is not necessarily still dialing. When a request fails, inspect the structured error:
Codes you are likely to see early include MEMBER_NOT_FOUND, CLAIM_NOT_FOUND, IVR_FAILURE, CLAIM_NUMBER_MISSING, and MEMBER_ID_INCORRECT. errorCategory is one of exactly two values, NOT_FOUND or SYSTEM_ERROR. errorCode is open-ended: new codes are added over time, so treat an unrecognized code as unclassified rather than validating against a fixed list. Missing-input codes follow the {FIELD}_MISSING pattern and incorrect-input codes follow {FIELD}_INCORRECT, which lets you auto-correct and resubmit (for example re-prompt for a member ID) instead of treating every non-success as a dead end.
Do not construct error codes from field names. The mapping is a fixed table, not a string transform, and several pairs are asymmetric: the provider address field yields PROVIDER_FACILITY_LOCATION_MISSING but PROVIDER_ADDRESS_INCORRECT. Match on the codes you receive, listed in Concepts.
Legacy already returned structured errors, so this is a reshaping rather than a brand-new concept. A failed legacy call carries both a top-level error string and an errors object:
errorClass carries values such as OTHER and BAD_NUMBER. Map errorClass to errorCode and errorCategory, errorJustification to errorMessage, and relevantInputKeys to the new missingFields. The type change matters: legacy error is a string, new error is an object. Code that logs error directly will start printing an object.

Listing

GET /v1/requests takes dateFrom and dateTo (YYYY-MM-DD), and the legacy batchId query parameter is renamed requestBatchId. With no date range and no batch ID, the default window is today only. The new endpoint also adds pagination, which legacy did not have: pass pageSize (default 100, maximum 500) and pageToken, and follow nextPageToken until it is absent. See Pagination. As before, transcripts and recording URLs are returned only when fetching a single request by ID, not in the list. Note that callSummary is present in list responses, so a summary field appearing in a list is expected rather than a sign you fetched detail.

6. Webhooks

Webhooks still fire on terminal states. The payload field names change, but the signature scheme is unchanged, so your existing verifier keeps working.

Configure the Request webhook URL

The Requests API has its own webhook URL, separate from the legacy Calls API webhook. Setting one does nothing for the other, and there is no fallback between them. Until you set the Request webhook URL, terminal requests send no webhook and no error: delivery is a silent no-op and your requests will look like they simply never completed.
Set it before you cut production traffic over. In the SuperDial portal, go to API, find the Webhooks section, and fill in Request webhook. Your existing legacy URL appears below it as Legacy webhook URL (/v1/calls), collapsed, and it keeps serving any traffic still going to POST /v1/calls. Both can be set at once during a phased cutover. To confirm delivery is working, the same panel shows Recent deliveries (7 days), filterable by Request ID. If you have submitted requests and that list is empty, the URL is not configured correctly. You can also pass webhookUrl in the body of POST /v1/requests to direct a single request elsewhere. A per-request value wins over the account-level setting, and it is frozen when the request is created: later changes to the account-level URL do not apply to requests already in flight. Full detail in Webhooks → Configuration. If neither is set, poll GET /v1/requests/{requestId} to get results.

Payload

Legacy above, new below.
Rename callId to requestId, batchId to requestBatchId, and read state instead of status, remembering the uppercase values and the new PARTIAL. Both payloads echo internalId and internalTag only when you supplied them. Payloads omit timestamps and results. On receipt, call GET /v1/requests/{requestId} to fetch the full record. Dedupe on requestId (legacy: callId).

Signature verification

Unchanged: HMAC-SHA256 over the raw request body, sent in the X-Webhook-Signature header as a bare hex digest. The server sends the body already sorted, so you can HMAC the raw request bytes directly and the same verifier works for both legacy and new webhooks. The signing secret depends on which credentials created the request. Production requests use your account webhookSecret if you have set one, otherwise your production API key. Sandbox requests use your sandbox API key and ignore webhookSecret entirely, which is a common cause of signature failures when testing. Because the server emits the body already sorted (json.dumps(payload, sort_keys=True)), re-parsing and re-serializing with sort_keys=True yields the same bytes as the raw request. That is why one verifier covers both legacy and new webhooks: you only need to read the renamed fields out of the parsed payload. Worked Python and Node examples are in Webhooks → Signature verification.

Delivery and retries

Delivery has two layers. On a 5xx or a connection failure we retry inline up to 3 more times (4 attempts total) with 0.5s, 1s, 2s backoff and a 10 second timeout per attempt. If those fail, the delivery is retried later: a request gets up to 3 delivery rounds within 7 days of reaching its terminal state.
Only 400 and 404 stop delivery permanently. Other 4xx responses, including 401 and 403, are retried across the remaining rounds, because a rotated credential can change the answer. If your guide or notes say “4xx is terminal”, that is not how it behaves.
Legacy webhook retry behaviour differs and does not transfer: it allows 3 attempts total, has no inline retry ladder, and applies no send timeout.

7. New capabilities worth adopting

  • Modality. modality tells you how a result was obtained: digital_only, phone_only, digital_plus_phone, or null when nothing has been dispatched yet. Some requests are now satisfied digitally with no phone call at all.
  • Idempotency. Resubmitting with the same internalId returns the previously created requestId instead of duplicating work. See Correlation and idempotency.
  • Partial results. PARTIAL plus missingFields lets you recover the fields that were captured instead of discarding the whole request.

8. Migration checklist

  • Set the Request webhook URL in the portal under API, Webhooks, Request webhook. This is separate from your legacy webhook URL and silently sends nothing until configured.
  • Auth is unchanged (GET /v1/auth, one-hour token). Reuse your existing token fetch and cache.
  • Repoint endpoints: /v1/calls to /v1/requests, /v1/calls/{id} to /v1/requests/{id}, /v1/scripts/... to /v1/schemas/....
  • Rename request fields: scriptId to schemaId, batch wrapper calls to requests.
  • Move the dialed number from top-level to into inputs.phoneNumber.
  • Rename response fields: callId to requestId, batchId to requestBatchId, and the list query parameter batchId to requestBatchId.
  • Rewrite result parsing: read named fields from results instead of question-index keys, resultsByAlias, or highLevelOutputs.
  • Handle PARTIAL, and read missingFields on it rather than error, which is null there.
  • Re-read errors from the new error object. It is an object, not a string, and errorCode is open-ended.
  • Handle batch status codes 200, 207, 400 and 500, and read the singular per-item error key.
  • Add pagination to any list polling: pageSize, pageToken, nextPageToken.
  • Update your webhook handler for the new field names and state values. Signature verification needs no change. Dedupe on requestId.
  • Validate end to end against sandbox credentials before moving production traffic.