Prerequisites
- An OptimalDial account at app.optimaldial.com.
- The owner role on at least one organization with an active subscription. (Spam-monitoring-only plans don’t include API access — you need a list-processing plan.)
- A terminal with
curl, or Node.js 18+ / Python 3.9+. - A publicly reachable HTTPS URL for receiving webhooks. For local development, ngrok, tailscale funnel, or Cloudflare tunnel all work.
1. Create an API key
- Sign in to the app as an organization owner.
- Open Developers → API keys.
- Click Create API key, name it (e.g.
local-test), and copy the full key (od_live_…) into your terminal:
2. Submit your first list
Uploads must contain at least 100 valid US or Canadian phone numbers. Numbers can be in any common shape (+15551234567, (555) 123-4567, 5551234567); we normalize them to E.164 server-side.
The simplest call is the JSON phone_numbers mode — no CSV required:
- cURL
- Node.js
- Python
credits_charged reflects the deduction from your organization’s balance — one credit per valid number. If you cancel before processing starts, the credits are refunded automatically.
3. Register a webhook so you get notified when processing finishes
PollingGET /api/v1/uploads/{id} works, but webhooks are cheaper and faster. Stand up an HTTPS endpoint that returns 2xx to any POST (any framework will do), then register it:
- cURL
- Node.js
- Python
POST /api/v1/webhooks:
- We send a
webhook.pingevent to the URL you provided. - If your endpoint responds
2xxwithin 5 seconds, the webhook is saved and marked active. Otherwise the call returns400and nothing is persisted — you can edit the URL and retry.
secret — a 64-character hex string used to verify HMAC signatures on every event we send. It is shown exactly once. Save it next to the API key.
4. Verify the next event you receive
Every webhook delivery carries anX-OptimalDial-Signature header:
f"{timestamp}.{body}" — Stripe-compatible, and the verifier is short:
- Node.js
- Python
webhook.ping arrive immediately when you register, then upload.created as soon as your call in step 2 returns. upload.completed arrives when processing finishes (typically minutes for small lists), with signed download URLs in the payload.
What’s next
- The full list of endpoints with every option: Uploads reference, Webhooks reference.
- A deeper dive on retries, headers, idempotency, and best practices: Receiving webhooks.
- Production-readiness checklist for handling errors and rate limits: Errors and rate limits.