From signup to a real outbound AI voice call in about 10 minutes. This guide walks through creating an API key, picking an agent, buying a number, verifying caller ID, and placing your first call.
Reference: the machine-generated API spec lives at /v1/openapi.json; browse it at /docs.
Go to app.callingscout.ai. Clerk handles auth; Google + email sign-in are both enabled. First sign-in auto-provisions a tenant and seeds the agent template library (17 templates across HR, sales, healthcare, insurance, etc.).
Your key is displayed like sk_live_Hx7f9… (or sk_test_Hx7f9… for sandbox). The prefix is how the server decides which mode you're in on every request:
| Prefix | Mode | What happens |
|---|---|---|
sk_live_ | Production | Real PSTN dials, real Stripe meter events, production webhook delivery, production analytics. |
sk_test_ | Sandbox | Twilio Magic Numbers only, no billing, only sandbox-flagged webhook subscriptions receive events. |
The first key has to come from the dashboard (bootstraps you). After that you can rotate programmatically:
curl https://api.callingscout.ai/api/v1/keys \
-H "Authorization: Bearer $CALLINGSCOUT_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "CI sandbox", "sandbox": true}'
Response:
{
"id": "3e9a8…",
"key_prefix": "sk_test_abcd",
"key": "sk_test_Hx7f9abc…",
"sandbox": true,
"created_at": "2026-04-24T…"
}
Store key in a secret manager. List, delete, rotate: GET /api/v1/keys, DELETE /api/v1/keys/{id}.
Every call to /api/v1/* needs Authorization: Bearer <key>:
curl https://api.callingscout.ai/api/v1/agents \
-H "Authorization: Bearer $CALLINGSCOUT_KEY"
On invalid keys you get 401 with a stable error code. Rate-limit hits return 429 with Retry-After.
Every mutating endpoint (POST / PATCH / PUT / DELETE) accepts an Idempotency-Key header. A retry within 24 hours with the same key returns the cached response byte-for-byte with X-Idempotent-Replay: true — the server does not re-execute. Use it on anything you'd hate to accidentally run twice (call creation, number purchase, campaign dispatch):
curl https://api.callingscout.ai/api/v1/calls \
-H "Authorization: Bearer $CALLINGSCOUT_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"agent_id": "…", "from_number": "+1…", "to_number": "+1…"}'
# List the 17 built-in templates (HR screening, sales discovery,
# collections, etc.)
curl https://api.callingscout.ai/api/v1/agent-templates \
-H "Authorization: Bearer $CALLINGSCOUT_KEY"
# Or list your existing agents
curl https://api.callingscout.ai/api/v1/agents \
-H "Authorization: Bearer $CALLINGSCOUT_KEY"
Create a custom one if you want:
curl https://api.callingscout.ai/api/v1/agents \
-H "Authorization: Bearer $CALLINGSCOUT_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Lead qualifier",
"pipeline_type": "cascaded",
"system_prompt": "You qualify inbound leads…",
"first_message": "Hey, thanks for reaching out.",
"voice_config": {
"stt": {"provider": "deepgram", "model": "nova-3", "params": {}},
"llm": {"provider": "openai", "model": "gpt-4.1-mini", "params": {}},
"tts": {"provider": "cartesia", "model": "sonic-3", "params": {}}
}
}'
Stick to the canonical provider matrix (docs/architecture/provider-matrix.md). Anything outside is marked "experimental" in the dashboard and is not covered by the nightly smoke test.
# Search
curl "https://api.callingscout.ai/api/v1/phones/available?country=US&area_code=415&provider=twilio" \
-H "Authorization: Bearer $CALLINGSCOUT_KEY"
# Provision
curl https://api.callingscout.ai/api/v1/phones/provision \
-H "Authorization: Bearer $CALLINGSCOUT_KEY" \
-H "Content-Type: application/json" \
-d '{"phone_number": "+14155552671", "provider": "twilio"}'
Platform-provisioned numbers are caller-ID verified automatically + attested at STIR/SHAKEN level A. You can dial from them immediately.
If you want to use a number you already own (not one we provisioned for you), you have to verify ownership over the phone:
# 1. Start — CallingScout sends a 6-digit code via SMS or voice
curl https://api.callingscout.ai/api/v1/verifications \
-H "Authorization: Bearer $CALLINGSCOUT_KEY" \
-H "Content-Type: application/json" \
-d '{"phone_number": "+14155552671", "channel": "sms"}'
# 2. Confirm — user reads the code, submits
curl https://api.callingscout.ai/api/v1/verifications/confirm \
-H "Authorization: Bearer $CALLINGSCOUT_KEY" \
-H "Content-Type: application/json" \
-d '{"phone_number": "+14155552671", "code": "123456"}'
Outbound dispatch will refuse a from_number until caller_id_verified_at is set on it. STIR/SHAKEN attestation B or C is also rejected for outbound.
curl https://api.callingscout.ai/api/v1/calls \
-H "Authorization: Bearer $CALLINGSCOUT_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"agent_id": "<agent-id>",
"from_number": "<your-verified-number>",
"to_number": "<recipient>"
}'
The call is dispatched asynchronously to a voice worker. You get 202 with the call id. Poll GET /api/v1/calls/{id} for status, or — better — receive a webhook on completion.
Fire-and-forget event delivery with per-subscription signing secrets and delivery history.
curl https://api.callingscout.ai/api/v1/webhooks \
-H "Authorization: Bearer $CALLINGSCOUT_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://your.app/hooks/callingscout", "event_types": ["*"]}'
Response gives you the signing secret (shown once). Every delivered event carries:
POST /hooks/callingscout
Content-Type: application/json
X-CallingScout-Event: call.completed
X-CallingScout-Event-Id: 7a1f…
X-CallingScout-Signature: t=1714000000,v1=9f0a…
Verify the signature (Stripe-pattern HMAC-SHA256):
import hmac, hashlib
def verify(body: bytes, header: str, secret: str, tolerance: int = 300) -> bool:
ts = dict(p.split("=", 1) for p in header.split(","))
signed = f"{ts['t']}.".encode() + body
expected = hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, ts["v1"])
Full event catalog: GET /api/v1/webhooks/event-types or docs/webhooks.md. Replay any past delivery with POST /api/v1/webhooks/deliveries/{id}/replay. Fire a synthetic test event with POST /api/v1/webhooks/{id}/test.
Official SDKs (hand-written Python + TypeScript, same pattern Stripe / Anthropic / Pipecat ship) are in development. Until they land, hit the REST API directly — it's small and stable.
import httpx
import uuid
CS = httpx.Client(
base_url="https://api.callingscout.ai/api/v1",
headers={"Authorization": f"Bearer {api_key}"},
)
# Create a call
r = CS.post(
"/calls",
json={
"agent_id": agent_id,
"from_number": "+14155552671",
"to_number": "+14155551234",
},
headers={"Idempotency-Key": str(uuid.uuid4())},
)
r.raise_for_status()
print(r.json()["status"]) # "queued"
const BASE = "https://api.callingscout.ai/api/v1";
const r = await fetch(`${BASE}/calls`, {
method: "POST",
headers: {
Authorization: `Bearer ${apiKey}`,
"Content-Type": "application/json",
"Idempotency-Key": crypto.randomUUID(),
},
body: JSON.stringify({
agent_id: agentId,
from_number: "+14155552671",
to_number: "+14155551234",
}),
});
const call = await r.json();
Both SDKs ship built-in retry with exponential backoff, cursor-paginating iterators, and a webhooks.verify() helper. Versions track API major version (callingscout@1.x speaks /v1/).
Pay-as-you-go, no subscription tiers. Every call shows a per-service cost breakdown on its detail page: STT / LLM / TTS / carrier / CallingScout overhead. Aggregates land in Stripe Meter Events after the call completes. Projected monthly bill + credit balance are on your dashboard.
sk_test_) calls are always $0.00.usage.credit_exhausted webhook 24h in advance and on zero.docs/concepts.md (agents, calls, campaigns, webhooks)docs/webhooks.md/docs/security/status