MamontCall

MamontCall API

Trigger outbound calls from your CRM, look up rates, check what a call cost, and get a webhook when it finishes — without ever touching the underlying phone system directly.

Already a MamontCall customer? Log in to the portal and open Developer to generate your API key.

Open the portal →

Authentication

Every request needs a Bearer key in the Authorization header:

Authorization: Bearer mc_live_...

The portal's Developer page can hold two active keys at once: generating a new key keeps the previous one working until you revoke it, so you can rotate keys with zero downtime. A machine-readable contract for this whole API lives at GET /v1/openapi.json (OpenAPI 3.1).

Place a call

POST/v1/click-to-call

Rings device first; once answered, bridges to destination. Limited to one call per device every 3 seconds.

curl -X POST https://api.mamontcall.com/v1/click-to-call \
  -H "Authorization: Bearer mc_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "device": "101",
    "destination": "+15551234567",
    "client_reference": "lead-42"
  }'

# 202 Accepted
{"ok": true, "call_id": "17834168392648973"}

Request body

FieldRequiredDescription
deviceYesThe device's SIP username / extension exactly as shown on the portal's Lines page (e.g. 9995552426) — not its numeric id
destinationYesThe number to bridge to once device answers
client_referenceNoYour own id, echoed back in the webhook (max 128 chars)

Idempotency & rate limits

Send an Idempotency-Key header (any unique string per intended call, max 128 chars) and a retried request within 10 minutes returns the original call_id with "replayed": true instead of dialing twice. Reusing a key with a different device/destination returns 409.

Limits: one call per device every 3 seconds, and 60 requests per key per minute — both answer 429 with Retry-After. A failed initiation does not consume the device throttle, so an immediate retry is allowed.

Errors

StatusMeaning
401Missing or invalid API key
400device or destination missing
404device not found on your account
409Idempotency-Key reused with different parameters
422The device has no CallerID and no DID, so there is no number to ring it back on — set one in the portal or contact support
429Device used within the last 3 seconds, or the key exceeded 60 requests/minute — see Retry-After
502The call could not be initiated

Fetch a recording

GET/v1/calls/{call_id}/recording

Returns the call's audio as audio/mpeg once available, scoped to your own account. Returns 404 if there's no recording yet or the id doesn't belong to you.

List & poll your calls

GET/v1/calls

Every click-to-call your account has placed, newest first — use it to reconcile against missed webhooks. Query params: since (ISO 8601 or epoch milliseconds), limit (1–200, default 50), offset.

curl "https://api.mamontcall.com/v1/calls?since=2026-07-30T00:00:00Z" \
  -H "Authorization: Bearer mc_live_..."

# 200 OK
{
  "total": 2,
  "calls": [
    { "call_id": "17854030425788856", "destination": "+16473035551", "initiated_at": "2026-07-30T09:17:22.720Z" },
    { "call_id": "17854029866509020", "destination": "+14378898460", "initiated_at": "2026-07-30T09:16:26.804Z" }
  ]
}

GET/v1/calls/{call_id}

Poll one call's outcome without webhooks. status is pending until the call record lands, then the same vocabulary as the webhook, plus duration_seconds and cost.

curl https://api.mamontcall.com/v1/calls/17854030425788856 \
  -H "Authorization: Bearer mc_live_..."

# 200 OK
{
  "call_id": "17854030425788856",
  "destination": "+16473035551",
  "initiated_at": "2026-07-30T09:17:22.720Z",
  "status": "answered",
  "duration_seconds": 34,
  "cost": 0.025
}

Check agent status

GET/v1/agents/status

Which of your own devices are online and which are currently on a call — for routing a click-to-call before dialing, or a live dashboard. Poll it; there's no per-request rate limit.

curl https://api.mamontcall.com/v1/agents/status \
  -H "Authorization: Bearer mc_live_..."

# optionally filter to specific devices:
curl "https://api.mamontcall.com/v1/agents/status?device=101,102" \
  -H "Authorization: Bearer mc_live_..."

# 200 OK
{
  "agents": [
    {
      "device": "101",
      "online": true,
      "on_call": true,
      "callback_ready": true,
      "destination": "+15551234567",
      "duration_seconds": 42,
      "last_call_end": "2026-07-07T09:34:41.000Z"
    },
    {
      "device": "102",
      "online": false,
      "on_call": false,
      "callback_ready": false,
      "destination": null,
      "duration_seconds": null,
      "last_call_end": null
    }
  ]
}

destination/duration_seconds are only non-null while on_call is true. last_call_end is when the device last finished a call, or null if it never has. callback_ready is false when the device has no CallerID and no DID — a click-to-call on it would return 422, so grey the button out instead.

Look up a rate

GET/v1/rates/lookup

The per-minute rate for a destination number on your account's rate plan, matched to the most specific dial prefix on file (e.g. a city code beats its country code when both exist).

curl "https://api.mamontcall.com/v1/rates/lookup?destination=15551234567" \
  -H "Authorization: Bearer mc_live_..."

# 200 OK
{
  "destination": "15551234567",
  "destination_name": "United States / Canada",
  "prefix": "1",
  "rate_per_minute": 0.012,
  "connection_fee": 0
}

Errors

StatusMeaning
400destination query param missing
404No rate plan on your account, or no prefix matches this destination

Look up a call's cost

GET/v1/calls/{call_id}/cost

The actual billed cost of a call already placed through POST /v1/click-to-call — not an estimate. call_id is the value returned when you placed the call.

curl https://api.mamontcall.com/v1/calls/17834168392648973/cost \
  -H "Authorization: Bearer mc_live_..."

# 200 OK — call has finished
{
  "call_id": "17834168392648973",
  "destination": "+15551234567",
  "duration_seconds": 184,
  "cost": 0.037,
  "status": "answered"
}

# 202 Accepted — call hasn't resolved yet, poll again shortly
{"call_id": "17834168392648973", "status": "pending"}

status is one of answered, no_answer, busy, failed, canceled — same vocabulary as the webhook below. Returns 404 if call_id is unknown or doesn't belong to your account.

Webhooks

When a call finishes, we POST a call.completed event to the webhook URL you configure in the portal:

POST <your webhook url>
Content-Type: application/json
X-MamontCall-Signature: sha256=<hmac>
X-MamontCall-Timestamp: <unix ms>

{
  "event": "call.completed",
  "event_id": "evt_17834168392648973",
  "call_id": "17834168392648973",
  "client_reference": "lead-42",
  "device": "101",
  "destination": "+15551234567",
  "status": "answered",
  "duration_seconds": 42,
  "initiated_at": "2026-07-07T09:33:59.000Z",
  "ended_at": "2026-07-07T09:34:41.000Z",
  "recording_available": true
}

status is one of answered, no_answer, busy, failed, canceled. We retry a failed delivery after 5s, 30s, then 120s. After 10 consecutive failed deliveries to your URL, delivery is suspended for the whole account — the portal's Developer page shows a warning with a one-click Re-enable delivery button once your endpoint is fixed. Saving a new webhook URL also resets the counter.

Verifying the signature

The webhook secret is shown in the portal's Developer page. Recompute the signature and compare:

const signed = timestamp + '.' + rawRequestBody
const expected = 'sha256=' + hmacSha256Hex(signed, webhookSecret)
// compare to the X-MamontCall-Signature header (constant-time compare)