"Likely Answer", "Likely Voicemail") once processing finishes. Use it when your tooling enriches one row at a time and the bulk Uploads API doesn’t fit.
It’s available to every account with an API key — no special access needed. Each contact costs 1 credit at the default standard tier, or 1.5x at max — see Validation tiers.
Lifecycle
POST /api/v2/contacts— submit one phone number. We charge credits per the validation tier (1 credit at the defaultstandard) and return immediately with acontact_idinqueuedstate.- Processing happens asynchronously. Typical turnaround is within 24-48 hours.
- When processing finishes, the contact transitions to
completed(withoptimaldial_statusset) orfailed(witherror_message). - You learn the result via either:
- The
callback_urlyou provided on creation (a signed POST per contact — recommended for integrations that can listen for webhooks), or - Your registered org-level webhook endpoints (
contact.completed/contact.failed), or - Polling
GET /api/v2/contacts/{contact_id}.
- The
The Contact object
Statuses
v2 — the current version
The identity fields below come from OptimalDial Identity, currently in public
beta. The response shape is stable; we’re still tuning the thresholds. Feedback
welcome at support@optimaldial.com.
/api/v2/contacts mirrors every v1 route — create, list, and retrieve — and
additionally reports the nine-tier OptimalDial_Status and the
Identity band.
Requests are identical — same fields, same credits, same idempotency semantics,
same callbacks. Only the response differs:
v1 is legacy — supported indefinitely, with no removal planned. It simply
doesn’t include identity:
optimaldial_status there keeps returning the Answer
Intent value, so existing filters keep working exactly as before.There is no migration. Both versions operate on the same contacts, so one
created on v1 can be read on v2 and vice versa. Point your calls at /api/v2/
whenever you’re ready — or don’t, and stay on v1.The two new fields are null on contacts whose batch was processed without
identity.contact.completed webhook payload gains optimaldial_tier and
optimaldial_identity alongside the existing optimaldial_status, which is
unchanged. Existing consumers need no action.
Validation tiers
Each contact runs at one of two tiers. Passvalidation_tier on Create a contact; it defaults to standard.
The value is case-insensitive, and anything other than
standard or max returns 400.
max is available on this endpoint at any volume, including a single contact. The 100-number minimum documented on the bulk Uploads API applies only there — if your pipeline produces a handful of numbers at a time, submit them here per-row rather than accumulating to reach that floor.
How max is billed per contact
Credits are whole-number ledger entries, so max can’t charge a fraction on a single-contact request. It bills marginally instead: successive max contacts are charged 2, 1, 2, 1, … credits.
The alternation runs across your current queued batch — the max contacts you’ve submitted that haven’t moved to processing yet. Whatever that batch ends up holding, its total is exactly ceil(N × 1.5): the same price as submitting those N numbers as one max upload.
So there’s no cost penalty for submitting one at a time instead of batching.
Because the charge alternates, the subtotal for any one run depends on where it lands in the batch: five
max contacts cost 8 credits when they open a fresh batch, or 7 when they land mid-alternation. A run of n is never charged more than ceil(n × 1.5). Read credits_charged on each response for the exact amount deducted rather than assuming a fixed per-contact cost.
Create a contact
201 with the new contact in queued status.
POST /api/v1/contacts accepts an identical request and is still supported — it
just returns the legacy response shape, without the
identity fields.Request body
- cURL
- Node.js
- Python
Response
201 Created:
Errors
Retrieve a contact
status == "completed".
List contacts
status filter.
Result delivery
When a contact reachescompleted or failed, OptimalDial fires up to two notifications:
- Per-contact callback — if you provided
callback_urlon creation, we POST a signed JSON body to it. Same signature scheme and retry semantics as org-level webhooks: HMAC-SHA256 off"{ts}.{body}"using yourcallback_secret, 6 attempts with exponential backoff. Headers includeX-OptimalDial-SignatureandX-OptimalDial-Timestamp. - Org-level webhooks — your registered webhook endpoints receive
contact.completed/contact.failedevents for every contact that finishes. Useful for fan-out to internal systems independent of the per-contact callback.
properties object on the result echoes back exactly what you sent on creation — use it to correlate the result with your own row IDs without storing the OptimalDial contact_id.