Skip to main content
All meaningful, customer-facing changes to the OptimalDial REST API and webhook contract are listed here. The api_version field on webhook payloads changes only when the payload shape itself changes.

2026-08-26 — Multiple phone columns per upload

  • Map up to 10 phone columns in one upload with the new phone_columns field — comma-separated in CSV mode, an array in JSON mode. See Multiple phone columns.
  • Billing is per number. A contact with a number in two mapped columns costs 2 credits at standard and ceil(2 × 1.5) = 3 at max. The same number appearing in more than one column — or already seen on an earlier contact — is de-duplicated and charged once.
  • New valid_number_count on the Upload object and in the upload.created webhook: the numbers you were charged for. valid_row_count keeps its meaning of contact rows, so do not derive an invalid count by subtracting one from the other on a multi-column upload.
  • The processed file keeps one row per contact and carries one {column}_OptimalDial_Status per mapped column (plus {column}_OptimalDial_Answer_Intent and {column}_OptimalDial_Identity on identity lists). A blank cell means that column held no number for that contact.
  • The 100-number minimum now counts numbers rather than contacts, so 50 contacts with two mapped columns each qualifies.
  • Nothing changes for single-column uploads. phone_column still works on its own, the result column is still the plain OptimalDial_Status, and valid_number_count equals valid_row_count. No api_version bump.

2026-08-10 — Upload cancellation removed

  • Breaking: DELETE /api/v1/uploads/{id} is removed. It now returns 410 Gone and no longer changes the upload or refunds credits. Submitting an upload is final — once we accept a list, we own the work and the refund decision.
  • The endpoint’s guarantees never held in practice: a cancel that arrived after processing began still flipped the upload to cancelled without refunding anything, and a repeated call could erase the record of a refund that had already been paid out.
  • If you submitted an upload in error, email support@optimaldial.com with the upload id and we’ll refund the credits where the work hasn’t been done.
  • The cancelled status stays in the Upload schema so historical uploads still parse. No new upload can enter it.

2026-07-03 — Credit balance endpoint

  • New Credit Balance APIGET /api/v1/credits/balance returns your organization’s current credit balance, split into subscription and purchased pools plus a total. Read-only, costs no credits, and available to every account with an API key. Use it to check remaining credits before submitting uploads or contacts.

2026-06-26 — Spam Monitoring API

  • New Spam Monitoring API — track your outbound numbers for carrier spam/scam-likely flagging across AT&T, T-Mobile, and Verizon. Add a number, complete verification, then read per-carrier status, run history, and screenshots, or get pushed alerts over webhooks. Available to every account with an API key and an active subscription.
  • New endpoints: POST /api/v1/spam/numbers, GET /api/v1/spam/numbers, GET /api/v1/spam/numbers/{id}, DELETE /api/v1/spam/numbers/{id}, POST /api/v1/spam/numbers/{id}/verify, POST /api/v1/spam/numbers/{id}/verify/resend, GET /api/v1/spam/numbers/{id}/history, and GET /api/v1/spam/numbers/{id}/screenshot (streams image/png).
  • SMS-or-call verification. Adding a number auto-initiates verification; submit the code to …/verify. Choose call (default) or sms — SMS isn’t deliverable to landlines, so fall back to call. Verification is required for monitoring to run, not a security gate.
  • Two new webhook events on the existing POST /api/v1/webhooks endpoint: spam.detectededge-triggered, fires once on a clean→spam transition per carrier (not on every daily re-test) — and number.verified. Same HMAC signing, headers, and retry semantics as existing events; no api_version bump.

2026-06-25 — Higher rate limits + per-row throughput

  • Write-endpoint rate limits were raised and restructured into token-bucket tiers: 600/min per API key (was 60) and 1,500/min per organization (was 600). POST /api/v1/contacts is now covered too, so high-volume per-row integrations (e.g. a Clay “Run column”) pace cleanly.
  • 429 responses gained a scope field (per_key | per_org) and X-RateLimit-Limit / X-RateLimit-Remaining / X-RateLimit-Reset headers; the observed field was removed. Retry-After is unchanged.
  • Buckets now refill continuously rather than resetting on a clock-aligned minute — burst up to the limit, then steady-state. Honor Retry-After and large jobs drain without errors.
  • High-volume partner integrations can have their per-organization limit raised on request.

2026-06-24 — MAX validation tier on the Contacts API

  • POST /api/v1/contacts accepts an optional validation_tier field (standard | max). Defaults to standard, so existing integrations are unaffected. An unrecognized value returns 400.
  • max has no minimum submission size here — the 100-number floor applies only to POST /api/v1/uploads. Per-row pipelines can request max one contact at a time.
  • Because credits are whole numbers, max bills marginally on this endpoint: successive max contacts are charged 2, 1, 2, 1, …, so a queued batch of N totals ceil(N × 1.5) — the same price as those N numbers submitted as one max upload. Read credits_charged per response for the exact amount. See Validation tiers.
  • The contact object and the contact.completed / contact.failed webhook payloads now include validation_tier. No api_version bump — the field is additive.
  • Documentation note: this field shipped on the date above but was missing from the Contacts API reference until 2026-08-11. The endpoint accepted max throughout; only the docs were incomplete.

2026-06-24 — Contacts API generally available

  • The Contacts API (POST /api/v1/contacts, GET /api/v1/contacts, GET /api/v1/contacts/{contact_id}) is now available to every account with an API key. It was previously limited to Enterprise organizations.
  • This access change altered no request or response shapes. Calls no longer return 403 feature_not_enabled; access is gated only by your API key and credit balance.

2026-06-05 — MAX validation tier

  • POST /api/v1/uploads accepts an optional validation_tier field (standard | max) in both CSV and JSON modes. Defaults to standard, so existing integrations are unaffected.
  • max is more thorough and is charged at 1.5 credits/number (billed as ceil(valid_row_count × 1.5)). See Validation tiers.
  • The upload object now includes a validation_tier field.

2026-04-23 — v1 GA

Initial general availability of the public API. New endpoints
  • POST /api/v1/uploads — submit phone-number lists in CSV (multipart) or JSON form.
  • GET /api/v1/uploads, GET /api/v1/uploads/{id} — list and retrieve uploads.
  • DELETE /api/v1/uploads/{id} — cancel an upload that hasn’t started processing.
  • GET /api/v1/uploads/{id}/download/{original|processed|processed-filtered} — short-lived signed download URLs.
  • POST /api/v1/webhooks, GET /api/v1/webhooks, GET /api/v1/webhooks/{id}, PATCH /api/v1/webhooks/{id}, DELETE /api/v1/webhooks/{id} — manage webhook endpoints.
  • GET /api/v1/webhooks/{id}/deliveries — recent delivery attempts for one endpoint.
Authentication
  • od_live_* API keys, scoped to a single organization. Owner-only minting via the in-app developer panel; SHA-256 hashed at rest; auto-revoked when the creating user leaves the org.
Webhooks
  • Three event types: upload.created, upload.completed, upload.failed.
  • HMAC-SHA256 signatures over {timestamp}.{body}, in Stripe-compatible t=…,v1=… format.
  • 5-minute replay window.
  • Up to 6 delivery attempts with backoff (1m, 5m, 30m, 2h, 12h between attempts).
  • Endpoints auto-disabled after 20 consecutive failures.
  • SSRF hardening: HTTPS-only, public IPs only, redirects not followed.
Rate limits
  • 60 requests/minute per API key, 600 requests/minute per organization, on write endpoints.