Dialfetch. REST API

Submit a goal and a phone number; Dialfetch places an AI phone call (navigating IVRs and holds), then returns the outcome, notes, structured answers, a transcript, and a recording. Base URL https://app.dialfetch.com.

Examples in

Authentication

Every /api/v1/* endpoint requires an API key (create one on the API Keys page). Keys look like df_… and go in the Authorization header as a bearer token. Keep them server-side — a key can place billable calls.

These examples read the key from a DIALFETCH_API_KEY environment variable:

export DIALFETCH_API_KEY="df_0123456789abcdef0123456789abcdef"

curl -s https://app.dialfetch.com/api/v1/credit \
  -H "Authorization: Bearer $DIALFETCH_API_KEY"

Create a call

POST /api/v1/calls. Only phone and goal are required. Pass an optional extract array to get typed, quoted answers back. Returns 201 with the new call id and status: "queued".

curl -s https://app.dialfetch.com/api/v1/calls \
  -H "Authorization: Bearer $DIALFETCH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "phone": "+15551234567",
    "goal": "Ask if my prescription is ready for pickup.",
    "context": "Pickup for Jane Doe, DOB 04/12.",
    "extract": [
      { "name": "ready", "type": "boolean" },
      { "name": "pickup_time", "type": "string" }
    ],
    "max_duration_minutes": 15
  }'

# -> {"id": "8b1f…", "status": "queued"}

Each extract field takes a name (a-z / 0-9 / underscore) and a type of string, number, boolean, or date — up to 25 fields, with an optional description to guide the agent.

Every goal is screened before dialing. Harassment, threats, impersonating a specific real person, pretexting, unsolicited sales/robocalls, and pranks are rejected with 422:

{"error": "call rejected", "reason": "Goal not allowed (sales): …", "retriable": false}

Placing a call also needs an active phone number (your caller ID) and enough credit for the call's estimated maximum cost:

422  {"error": "no_active_number"}                        // buy one first — see Numbers below
402  {"error": "insufficient_credit", "requiredCents": 500} // add credit — see Credit below

Calls to destinations outside 8:00–20:00 local time (inferred from the area code) are accepted but held in queued until the window opens. Optionally pass from_number_id to choose which of your active numbers is the caller ID (default: your oldest active number), and webhook_url to override your default result webhook.

Get & poll a call

GET /api/v1/calls/:id returns the full call. A call runs asynchronously — poll it (or set a webhook) until status reaches a terminal state (completed, failed, or canceled).

# Fetch once
curl -s https://app.dialfetch.com/api/v1/calls/CALL_ID \
  -H "Authorization: Bearer $DIALFETCH_API_KEY"

# Poll until it finishes
until curl -s https://app.dialfetch.com/api/v1/calls/CALL_ID \
  -H "Authorization: Bearer $DIALFETCH_API_KEY" \
  | grep -qE '"status": ?"(completed|failed|canceled)"'; do
  sleep 5
done

A completed call looks like:

{
  "id": "8b1f…",
  "phone": "+15551234567",
  "goal": "Ask if my prescription is ready for pickup.",
  "status": "completed",       // queued | dispatching | dialing | ivr | on_hold |
                               // in_conversation | wrapping_up | completed | failed | canceled
  "outcome": "accomplished",   // accomplished | partial | failed | no_answer |
                               // refused | voicemail | ivr_dead_end
  "confidence": 0.92,
  "notes": ["Prescription is ready; pickup window ends at 6pm."],
  "answers": {
    "ready": {"value": true, "quote": "yes it's ready for pickup"},
    "pickup_time": {"value": "before 6pm", "quote": "come by before six"}
  },
  "transcript": [{"speaker": "agent", "text": "…"}, {"speaker": "human", "text": "…"}],
  "recording_url": "https://…",
  "duration_seconds": 143,
  "cost": {"total_usd": 0.18},
  "events": [{"event": "queued", "at": "…"}, {"event": "dialing", "at": "…"}]
}

List calls

GET /api/v1/calls — newest first, cursor-paginated. Filter by status; page with limit (1–100, default 25) and cursor (the previous page's next_cursor).

curl -s "https://app.dialfetch.com/api/v1/calls?status=completed&limit=25" \
  -H "Authorization: Bearer $DIALFETCH_API_KEY"

Cancel a call

DELETE /api/v1/calls/:id. A queued call is canceled immediately; an active call gets a cancel request sent to the voice engine and its final state arrives shortly after via the normal result flow.

curl -s -X DELETE https://app.dialfetch.com/api/v1/calls/CALL_ID \
  -H "Authorization: Bearer $DIALFETCH_API_KEY"

Numbers & credit

Usage is billed in integer cents (USD) from a prepaid balance. At dispatch the estimated maximum cost is held; when the call finishes the hold is released and the actual cost is charged (base fee + per-minute for answered/voicemail calls; nothing for no-answer / failed / ivr_dead_end). You need at least one active number as your caller ID.

# Search for a number by area code
curl -s https://app.dialfetch.com/api/v1/numbers/search \
  -H "Authorization: Bearer $DIALFETCH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"areaCode": "415"}'

# Provision one (becomes a caller ID)
curl -s https://app.dialfetch.com/api/v1/numbers \
  -H "Authorization: Bearer $DIALFETCH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"phoneNumber": "+14155550123"}'

# List your numbers / check credit / see prices
curl -s https://app.dialfetch.com/api/v1/numbers  -H "Authorization: Bearer $DIALFETCH_API_KEY"
curl -s https://app.dialfetch.com/api/v1/credit   -H "Authorization: Bearer $DIALFETCH_API_KEY"
curl -s https://app.dialfetch.com/api/v1/pricing  -H "Authorization: Bearer $DIALFETCH_API_KEY"

POST /api/v1/credit/deposit with {"amountCents": 2000} returns a Stripe checkoutUrl, or 501 if Stripe isn't configured (an admin funds you directly). Number endpoints require Twilio (501 otherwise). DELETE /api/v1/numbers/:id releases a number.

Result webhook

When a call finishes and a webhook_url is set (per call, or your default in settings), Dialfetch POSTs the full call object — the same shape as GET /api/v1/calls/:id. Delivery is retried 3 times with exponential backoff. Each delivery is signed: the x-dialfetch-signature header is the hex HMAC-SHA256 of the raw request body, keyed with the SHA-256 hash of your most recently created active API key. Always verify it before trusting the payload.

No cURL example for this step — switch the tab above to cURL, Python, or Node.js.

Errors

400  validation error            {"error": "…"}
401  missing/invalid API key      {"error": "unauthorized"}
402  not enough credit            {"error": "insufficient_credit", "requiredCents": 500}
404  not found / not yours        {"error": "not found"}
409  call already finished        {"error": "call is already completed"}
422  guardrails rejected the goal {"error": "call rejected", "reason": "…", "retriable": false}
422  no active caller-ID number   {"error": "no_active_number"}

Dialfetch discloses on every call that it is an AI assistant calling on behalf of a client, and honors do-not-call requests automatically.