Overview
The API is plain HTTPS + JSON. All endpoints live under one base URL:
https://crushitengine.com/api/v1
There are three kinds of endpoints: webhooks (we POST events to your URL as they happen), actions (you change something), and searches (you look something up). Everything is scoped to your business — an API key can only ever see and touch its own account.
Authentication
Every request sends your API key as a bearer token. Generate it (or rotate it) in the dashboard under Integrations → Automation. The same key powers the Zapier app and this API.
curl https://crushitengine.com/api/v1/me \ -H "Authorization: Bearer YOUR_API_KEY"
- A bad or missing key returns
401. - Rotating the key invalidates the old one immediately and disconnects any webhook subscriptions — resubscribe with the new key.
- Treat the key like a password: server-side only, never in a browser or an app you ship.
Conventions & errors
- Request and response bodies are JSON. Send
Content-Type: application/json. - Phone numbers are accepted in any format and normalized to E.164 (
+18135551234). US 10-digit numbers get+1automatically. - Timestamps are ISO 8601, UTC.
- Errors return
{ "ok": false, "error": "…" }with a 400 (your input), 401 (your key), 403 (your plan or products don't include that action), 404 (not found), or 500 (us) status. Success responses carry"ok": true. - A compliance-blocked text is not an error — see the compliance model.
- No hard rate limits today; keep it civil (bursts under a few requests per second). Abusive traffic gets keys revoked.
Webhooks
Subscribe a URL to an event and we POST the payload the moment it happens. Subscribe once per event (up to 100 active subscriptions):
POST/hooks
curl -X POST https://crushitengine.com/api/v1/hooks \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"event": "lead_captured",
"target_url": "https://example.com/hooks/crushitengine",
"filter": { "lead_status": "hot" }
}'
→ { "ok": true, "id": "SUBSCRIPTION_ID" }target_urlmust be HTTPS.filteris optional: string equality against top-level payload fields, case-insensitive. The example only delivers hot leads.- Every delivery is a JSON POST wrapped in a stable envelope:
id(unique per event — deduplicate on it),event, andbusiness_id, plus the event's own fields. - Delivery is at-most-once: one POST, 5-second timeout, no automatic retries. If your endpoint must not miss data, also poll the searches, or use Zapier, which manages retries for you.
- Webhooks only fire while the account is on AI Voice Scale. After a downgrade your subscriptions are kept but nothing is sent until the account is back on Scale.
- Respond 2xx quickly. Responding
410 Gonepermanently deletes the subscription.
Unsubscribe with the id you got back:
DELETE/hooks/{id}
While building, fetch recent real examples of any event's payload, shaped exactly like live deliveries (falls back to a static sample when your account has no matching data yet):
GET/samples/{event}
Event reference
All 27 events, with example payloads. Every payload also carries the envelope fields event and business_id.
call_startedAn inbound call hit your number (fires at the start of the call, before any outcome is known).
{
"id": "00000000-0000-0000-0000-00000000000a",
"caller_number": "+18135551234",
"business_number": "+18135550000",
"at": "2026-09-08T15:04:00Z",
"trusted_caller": false
}call_completedA call ended and was processed — includes outcome, sentiment, duration, and the full transcript text.
{
"id": "00000000-0000-0000-0000-000000000001",
"call_id": "00000000-0000-0000-0000-000000000001",
"direction": "inbound",
"caller_number": "+18135551234",
"business_number": "+18135550000",
"started_at": "2026-09-08T15:04:05Z",
"duration_sec": 142,
"outcome": "lead",
"sentiment": "positive",
"sentiment_note": "Caller was excited to get scheduled.",
"topics": [
"Water Heater",
"Quote Request"
],
"transcript_text": "Agent: Thanks for calling! ...\nCaller: I need a quote...",
"recording_url": "https://crushitengine.com/api/recordings/example",
"returning_caller": false,
"prior_calls": 0
}missed_callA call went unanswered, or the caller hung up before speaking.
{
"id": "00000000-0000-0000-0000-000000000003",
"call_id": "00000000-0000-0000-0000-000000000003",
"caller_number": "+18135551234",
"business_number": "+18135550000",
"at": "2026-09-08T15:10:00Z",
"reason": "no_answer"
}voicemail_receivedA voicemail was recorded on a missed call, with its transcription and recording link.
{
"id": "00000000-0000-0000-0000-00000000000b",
"call_id": "00000000-0000-0000-0000-000000000003",
"caller_number": "+18135551234",
"transcription": "Hi, this is Maria — call me back about the estimate.",
"recording_url": "https://example.telnyx.com/recordings/example.mp3",
"at": "2026-09-08T15:12:00Z"
}lead_capturedA new lead came in — from the AI on a call, the team phone, a marketplace (Speed-to-Lead), your website widget (“Text us”, or a call-back request from the “Ask us” assistant), or your digital business card. Includes contact details, what they wanted, a summary, the `source`, who referred them (`referred_by`), and for card leads the intake answers.
{
"id": "00000000-0000-0000-0000-000000000002",
"lead_id": "00000000-0000-0000-0000-000000000002",
"caller_full_name": "Maria Torres",
"business_name": "Torres Landscaping",
"business_type": "landscaping",
"phone_number": "+18135551234",
"email": "maria@example.com",
"service_area": "Tampa, FL",
"product_interest": "Weekly lawn care",
"lead_status": "hot",
"call_summary": "Wants weekly service for a half-acre lot, ready to start.",
"notes": "Prefers a morning call back.",
"source": "angi",
"channel": "angi",
"lead_cost_cents": 3800,
"external_ref": "ANGI-10293",
"referred_by": "Dave Kim",
"card_id": null,
"card_name": null,
"intake": null,
"emergency": false,
"created_at": "2026-09-08T15:06:00Z"
}rescue_sentA missed-call text-back message went out. `touch` is rescue, followup, or reengage; `source` is template or ai_drafted; `channel` is sms, or email when it went by the email backup (landline or bounced text).
{
"id": "00000000-0000-0000-0000-000000000004",
"caller_number": "+18135551234",
"body": "Sorry we missed your call! This is Tampa Test Plumbing - how can we help?",
"source": "template",
"touch": "rescue",
"channel": "sms",
"first_contact": true,
"thread_id": "00000000-0000-0000-0000-000000000005",
"at": "2026-09-08T15:10:45Z"
}caller_repliedA customer texted back for the first time on a conversation thread.
{
"id": "00000000-0000-0000-0000-000000000005",
"thread_id": "00000000-0000-0000-0000-000000000005",
"caller_number": "+18135551234",
"body": "Yes! I need someone out this week.",
"at": "2026-09-08T15:14:00Z"
}sms_receivedAny inbound text message from a customer.
{
"id": "00000000-0000-0000-0000-00000000000c",
"thread_id": "00000000-0000-0000-0000-000000000005",
"caller_number": "+18135551234",
"body": "What time can you come out?",
"photo_count": 0,
"first_reply": false,
"at": "2026-09-08T15:16:00Z"
}opted_outA customer texted STOP (or was suppressed). Texting them is now blocked platform-wide.
{
"id": "00000000-0000-0000-0000-000000000006",
"caller_number": "+18135551234",
"action": "opted_out",
"at": "2026-09-08T15:20:00Z"
}opted_inA customer texted START and restored their texting consent.
{
"id": "00000000-0000-0000-0000-000000000007",
"caller_number": "+18135551234",
"action": "opted_in",
"at": "2026-09-08T15:25:00Z"
}unanswered_questionThe agent was asked something outside its knowledge — the exact question, ready to be answered and taught back.
{
"id": "00000000-0000-0000-0000-000000000008",
"question": "Do you install water softeners?",
"call_id": "00000000-0000-0000-0000-000000000001",
"at": "2026-09-08T15:30:00Z"
}appointment_bookedAn appointment was booked — by the AI on a call or in a text conversation, or from your digital business card.
{
"id": "00000000-0000-0000-0000-000000000009",
"appointment_id": "00000000-0000-0000-0000-000000000009",
"starts_at": "2026-09-10T14:00:00Z",
"ends_at": "2026-09-10T15:00:00Z",
"customer_name": "Maria Torres",
"customer_phone": "+18135551234",
"service": "Estimate visit",
"source": "angi",
"at": "2026-09-08T15:35:00Z"
}appointment_requestedA caller wanted to book but no open time worked, so your AI took a request for the office to schedule. sent_to names the job software it landed in as an unscheduled job (Jobber, Housecall Pro or ServiceTitan), or is null.
{
"id": "00000000-0000-0000-0000-000000000020",
"customer_name": "Maria Torres",
"customer_phone": "+18135551234",
"service": "Water heater replacement",
"preferred_time": "Saturday morning",
"sent_to": "Jobber",
"at": "2026-10-11T15:35:00Z"
}appointment_cancelledAn appointment was cancelled (dashboard or API). Its reminder text is called off automatically; `waitlist_offered` says whether the next person on your waitlist was texted about the open slot.
{
"id": "00000000-0000-0000-0000-000000000017",
"appointment_id": "00000000-0000-0000-0000-000000000009",
"starts_at": "2026-09-10T14:00:00Z",
"customer_name": "Maria Torres",
"customer_phone": "+18135551234",
"service": "Estimate visit",
"reminder_cancelled": true,
"waitlist_offered": true,
"cancelled_via": "dashboard",
"at": "2026-09-09T11:00:00Z"
}returning_callerA number that has called before called again — includes the prior-call count and last call date.
{
"id": "00000000-0000-0000-0000-00000000000d",
"call_id": "00000000-0000-0000-0000-000000000001",
"caller_number": "+18135551234",
"prior_calls": 2,
"last_call_at": "2026-09-01T12:00:00Z",
"at": "2026-09-08T15:04:05Z"
}negative_sentiment_callA completed call scored negative sentiment — a caller worth a personal follow-up.
{
"id": "00000000-0000-0000-0000-00000000000e",
"call_id": "00000000-0000-0000-0000-000000000001",
"caller_number": "+18135551234",
"sentiment": "negative",
"sentiment_note": "Caller was frustrated about a missed appointment.",
"at": "2026-09-08T15:40:00Z"
}transfer_failedA live transfer to you didn't connect (the agent fell back to taking a message/callback).
{
"id": "00000000-0000-0000-0000-00000000000f",
"call_id": "00000000-0000-0000-0000-000000000001",
"caller_number": "+18135551234",
"at": "2026-09-08T15:45:00Z"
}call_ratedYou rated a call thumbs up or down in the dashboard.
{
"id": "00000000-0000-0000-0000-000000000010",
"call_id": "00000000-0000-0000-0000-000000000001",
"rating": "down",
"caller_number": "+18135551234",
"at": "2026-09-08T15:50:00Z"
}review_receivedA new review synced in from a connected profile (e.g. Google) — includes platform, rating, author, and text.
{
"id": "00000000-0000-0000-0000-000000000011",
"platform": "google",
"author": "Maria Torres",
"rating": 5,
"text": "Alex answered right away and got someone out same day. Fantastic service!",
"at": "2026-09-08T16:00:00Z"
}negative_reviewA new review at or below 3 stars landed — worth a personal, timely response.
{
"id": "00000000-0000-0000-0000-000000000012",
"platform": "google",
"author": "Sam Vale",
"rating": 2,
"text": "Took a while to get scheduled.",
"at": "2026-09-08T16:05:00Z"
}review_request_sentA review request was scheduled/sent to a customer, with the review link and source.
{
"id": "00000000-0000-0000-0000-000000000013",
"caller_number": "+18135551234",
"customer_name": "Maria Torres",
"link": "https://g.page/r/example/review",
"source": "api",
"at": "2026-09-08T16:10:00Z"
}referral_sentA referral ask was scheduled for a customer you served — after a job, or right after they left a 4–5★ review (`source` is appointment, happy_review, api, or manual). Part of Marketing (AI Voice Pro & Scale).
{
"id": "00000000-0000-0000-0000-000000000014",
"caller_number": "+18135551234",
"customer_name": "Maria Torres",
"kind": "referral",
"source": "api",
"at": "2026-09-10T16:10:00Z"
}rebook_scheduledA repeat-booking reminder was scheduled for a past customer, with its send date (`scheduled_for`). Part of Marketing (AI Voice Pro & Scale).
{
"id": "00000000-0000-0000-0000-000000000015",
"caller_number": "+18135551234",
"customer_name": "Maria Torres",
"kind": "rebook",
"source": "appointment",
"scheduled_for": "2027-03-10T16:10:00Z",
"at": "2026-09-10T16:10:00Z"
}crew_updateSomeone on the team tapped a crew update in team chat — running late, parts needed, need help, or job done. Includes who sent it, the message, and the customer's number when it was sent in a customer conversation.
{
"id": "00000000-0000-0000-0000-000000000018",
"conversation_id": "00000000-0000-0000-0000-000000000019",
"update": "parts_needed",
"label": "Parts needed",
"message": "Parts needed — need a 3/4 ball valve",
"from": "Mike",
"customer_number": "+18135551234",
"at": "2026-09-27T15:10:00Z"
}feedback_receivedA customer answered the private "how did we do? reply 1 to 5" check-in (Reputation). Includes the score, `low_score` (3 or under), and any comment.
{
"id": "00000000-0000-0000-0000-000000000021",
"customer_name": "Maria Torres",
"customer_phone": "+18135551234",
"score": 2,
"low_score": true,
"comment": "Tech was late and didn't call ahead",
"at": "2026-10-12T15:35:00Z"
}unbooked_callA caller clearly wanted a job, quote or appointment but the call ended without one — a second-chance lead. Includes the AI's reason (e.g. "price too high") and a summary.
{
"id": "00000000-0000-0000-0000-000000000017",
"call_id": "00000000-0000-0000-0000-000000000001",
"caller_number": "+18135551234",
"reason": "price too high",
"summary": "Asked what a water heater replacement costs, then said they'd think about it.",
"team_call": false,
"at": "2026-09-08T15:50:00Z"
}lead_escalatedA texting customer was handed off to a human (asked for a person, urgent, or unhandled) — includes the reason and marketplace `source`.
{
"id": "00000000-0000-0000-0000-000000000016",
"thread_id": "00000000-0000-0000-0000-000000000005",
"caller_number": "+18135551234",
"customer_name": "Maria Torres",
"reason": "asked to speak with a person",
"channel": "sms",
"source": "angi",
"at": "2026-09-08T15:18:00Z"
}Actions
Verify your key
GET/me
Confirms the API key works and identifies the business it belongs to — including its plan and the products it owns, so you know which actions apply.
→ { "ok": true, "business_id": "…", "business_name": "Tampa Test Plumbing", "plan": "scale", "products": ["ai_voice", "mctb"], "marketing_included": true }Send a text message
POST/send-text
Texts a customer from your business number. Every send passes the same compliance gate as the platform's own messages — opt-outs and quiet hours are re-checked at the instant of send, and unregistered numbers never send into carrier filtering.
| Field | Required | Description |
|---|---|---|
| to | Yes | Customer phone number, any format. |
| message | Yes | Message text (up to 1,000 characters). Optional when media_url is set. |
| media_url | — | A public https link to one photo (JPG, PNG or GIF, up to 5 MB) — sends a picture text (MMS). |
| send_at | — | ISO date-time to send later instead of now (1 minute to 90 days ahead, words only). Returns `status: "scheduled"` and a `scheduled_id`. Opt-outs, quiet hours and registration are re-checked when it sends. |
→ { "ok": true, "status": "sent", "to": "+18135551234", "thread_id": "…" }A blocked send is NOT an error — you get 200 with `status: "suppressed"` and a `reason`: `contact_opted_out`, `quiet_hours`, or `carrier_registration_pending`. Branch on `status`, don't retry.
Sent messages appear in your dashboard inbox thread like any other outbound text.
Open or close a job's team chat
POST/job-chat
Automatic job chats from any field software: open a team chat for a dispatched job with the assigned techs (matched to team logins by email) plus the owner and managers, and a first message with the customer, address and job summary. Calling open again refreshes it; close marks it done and keeps the history. Team messaging comes with AI Voice or Cloud PBX; without either, this returns 403.
| Field | Required | Description |
|---|---|---|
| action | Yes | "open" or "close". |
| job_id | Yes | Your system's job id. |
| source | — | Where the job lives (jobber, housecall, servicetitan…). Defaults to "api". |
| job_number, title, customer_name, customer_phone, address, summary | — | Details shown in the chat's first message (open only). |
| assignee_emails | — | Array (or comma-separated) of the techs' emails; each who has a team login is added. |
→ { "ok": true, "conversation_id": "…", "created": true }Add a note to a customer
POST/customer-note
Adds a note to a customer's timeline — what was promised, a gate code, anything the team should see. Shows in the dashboard with who wrote it.
| Field | Required | Description |
|---|---|---|
| phone | Yes | Customer phone number, any format. |
| note | Yes | The note (up to 4,000 characters). |
| author | — | Who wrote it. Defaults to "API". |
→ { "ok": true, "phone": "+18135551234" }Create or update a contact/lead
POST/contacts
Phone-keyed upsert into your leads. If a lead with that phone number exists, only the fields you send are overwritten; otherwise a new lead is created.
| Field | Required | Description |
|---|---|---|
| phone_number | Yes | Any format; normalized to E.164. |
| caller_full_name | — | Contact name. |
| business_name | — | Their company, if B2B. |
| business_type | — | Their industry. |
| — | Email address. | |
| service_area | — | Where they are. |
| product_interest | — | What they want. |
| lead_status | — | hot, warm, or cold. |
| call_summary | — | Summary text. |
| notes | — | Free-form notes. |
→ { "ok": true, "created": true, "lead": { … } }Teach the agent a fact
POST/knowledge
Adds a fact to your live agent's knowledge as a coaching note and republishes the agent — the change is live on the very next call, usually within seconds.
| Field | Required | Description |
|---|---|---|
| fact | Yes | The fact, in plain English (5–500 characters). |
→ { "ok": true, "status": "applied_live", "note_id": "…" }If the live republish can't complete, you get `status: "saved_pending"` — the note is saved and applies on the next Save & publish from the dashboard.
Pause or resume the AI
POST/pause
One-tap pause as an API: while paused, calls ring your real phone — and with Missed-Call Text Back, the ones you miss still get a text.
| Field | Required | Description |
|---|---|---|
| paused | Yes | true to pause, false to resume. |
→ { "ok": true, "paused": true }Pausing requires a transfer phone (Phone line → Transfer) — paused calls have to ring somewhere. With the Team Phone on, paused calls go to your menu and team instead, so no transfer phone is needed.
Set the answering mode
POST/answering-mode
Switches how your number answers, exactly like the dashboard switch.
| Field | Required | Description |
|---|---|---|
| mode | Yes | ai_always, mctb_always, ai_after_hours, or mctb_after_hours. |
→ { "ok": true, "mode": "ai_after_hours" }Every mode except ai_always uses text-back, so it needs Missed-Call Text Back on the account (403 otherwise). The two scheduled modes also need AI Voice Pro or Scale.
With the Team Phone (Cloud PBX) on, the phone system owns the number and a separate front-desk setting in the dashboard decides who answers — this returns 409 and changes nothing.
Add a trusted number
POST/trusted-numbers
Trusted callers skip the AI and ring you directly, and are never sent text-back messages. 25 numbers max.
| Field | Required | Description |
|---|---|---|
| phone | Yes | Any format. |
| label | — | A name for the entry (up to 60 characters). |
→ { "ok": true, "entry": { … } }Remove a trusted number
DELETE/trusted-numbers
Removes a number from the trusted list (send the phone in the JSON body).
| Field | Required | Description |
|---|---|---|
| phone | Yes | Any format. |
→ { "ok": true, "removed": "+18135551234" }Suppress a contact (do not text)
POST/suppress
Marks a number as opted out here — use it to sync unsubscribes from your CRM or email platform so no channel texts someone who said stop anywhere.
| Field | Required | Description |
|---|---|---|
| phone | Yes | Any format. |
→ { "ok": true, "suppressed": "+18135551234" }One-way by design: under FCC revocation rules only the customer texting START restores consent, so there is no unsuppress endpoint.
Turn text-back on or off
POST/text-back
Flips the missed-call rescue engine — for example, off while a vacation auto-responder runs.
| Field | Required | Description |
|---|---|---|
| active | Yes | true or false. |
→ { "ok": true, "active": false }Turning it on needs Missed-Call Text Back on the account (403 otherwise). Turning it off always works.
Change appointment reminders
POST/reminder-settings
The reminder text every booked customer gets before their appointment — on by default, no text-back needed. GET the same path to read the current settings.
| Field | Required | Description |
|---|---|---|
| enabled | — | true or false. |
| hours_before | — | 2, 4, 24, or 48. |
→ { "ok": true, "updated": ["reminder_hours_before"] }Create an appointment
POST/appointments
Writes a confirmed appointment in from your booking tool. Callers with an upcoming appointment are automatically spared sales-y rescue texts.
| Field | Required | Description |
|---|---|---|
| starts_at | Yes | ISO 8601 date-time. |
| ends_at | — | ISO 8601 date-time. |
| customer_name | — | Customer name. |
| customer_phone | — | Any format. |
| service | — | What the appointment is for. |
| external_ref | — | Your system's id, for cross-referencing. |
→ { "ok": true, "appointment": { … } }Appointments created here never emit the appointment_booked webhook — no event loops.
Rate a call
POST/calls/{id}/rate
Rate a call as the owner. Down-ratings flag the call for quality review.
| Field | Required | Description |
|---|---|---|
| rating | Yes | up or down. |
→ { "ok": true, "call_id": "…", "rating": "down" }Answer or dismiss a question
POST/questions/{id}
Close an open unanswered question. Answering turns your answer into agent knowledge and republishes the live agent — the teach-it loop as an API.
| Field | Required | Description |
|---|---|---|
| action | Yes | answer or dismiss. |
| answer | — | Required for answer: the answer in plain English (3–400 chars). |
→ { "ok": true, "question_id": "…", "status": "applied_live" }Take over / hand back a conversation
POST/thread
Toggle who handles one text thread. Taking over (ai_active=false) stops AI replies and cancels queued follow-ups for that customer.
| Field | Required | Description |
|---|---|---|
| phone | Yes | Customer number (or pass id). |
| ai_active | Yes | false = owner handles it; true = AI resumes. |
→ { "ok": true, "thread_id": "…", "ai_handling": false }Set business hours
POST/business-hours
Local hours that drive after-hours answering modes and what the agent tells callers. Republishes the live agent.
| Field | Required | Description |
|---|---|---|
| open_hour | — | 0–23, local. |
| close_hour | — | 1–24, local; must be after open_hour. |
| days_iso | — | Array of days, 1=Mon … 7=Sun. |
→ { "ok": true, "hours": { … }, "agent_republished": true }Set the transfer number
POST/transfer-number
Where hot calls and pass-through modes ring. Republishes the live agent (the number is baked into its transfer tool).
| Field | Required | Description |
|---|---|---|
| phone | Yes | Any format. |
→ { "ok": true, "transfer_number": "+1813…", "agent_republished": true }Update text-back settings
POST/textback-settings
Partial update — only fields you send change. Quiet hours are a fixed compliance floor and not settable.
| Field | Required | Description |
|---|---|---|
| template_open_hours | — | Rescue text during business hours. |
| template_closed_hours | — | Rescue text after hours. |
| rescue_delay_seconds | — | 0–300. |
| followups_enabled | — | true/false. |
| followup_delay_minutes | — | 30–360. |
| next_day_reengage_enabled | — | true/false. |
| followup_text | — | Follow-up message text. |
| ai_callback_enabled | — | true/false — the AI receptionist calls missed callers back (needs AI Voice Pro or Scale with caller recognition on). |
| email_fallback_enabled | — | true/false — email the missed-call message instead when the caller is on a landline or the text bounces (only to an email you already have). |
→ { "ok": true, "updated": ["rescue_delay_seconds"] }Cancel queued follow-ups
DELETE/followups
Cancel one queued follow-up by id, or everything queued for a phone number.
| Field | Required | Description |
|---|---|---|
| id | — | One follow-up's id. |
| phone | — | …or a customer number (cancels all queued for them). |
→ { "ok": true, "cancelled": 2 }Cancel an appointment
DELETE/appointments/{id}
Marks the appointment cancelled (kept in history), calls off its reminder text, offers the slot to the next person on your waitlist, and fires the appointment_cancelled webhook.
→ { "ok": true, "appointment_id": "…", "status": "cancelled" }Send a review request
POST/review-request
Schedule the compliant post-job Google-review text to a customer (same link to everyone; opt-out, quiet-hours and A2P gated). Optional follow-up rides the queue.
| Field | Required | Description |
|---|---|---|
| phone | Yes | Customer mobile, any common format |
| name | — | Customer name (first name is used) |
| technician | — | Technician name for the {tech} template variable (`tech` also accepted) |
→ { "ok": true, "status": "scheduled" }If it can't be sent you get `status: "skipped"` with a `reason` (for example disabled, opted_out, already_sent) — not an error.
Send a referral ask
POST/referral-request
Text a customer you just served a friendly "know someone who could use us?" message (opt-out, quiet-hours and A2P gated like every send). Part of Marketing — AI Voice Pro & Scale — with referral asks switched on in Marketing → Referrals. One ask per customer every 90 days.
| Field | Required | Description |
|---|---|---|
| phone | Yes | Customer mobile |
| name | — | Customer name |
→ { "ok": true, "status": "scheduled" }Otherwise `status: "skipped"` with a `reason`: not_included (plan), disabled, already_sent, opted_out.
Schedule a repeat-booking reminder
POST/rebook
Queue a months-out "time to get back on the schedule" text. Part of Marketing — AI Voice Pro & Scale — with repeat-booking on in Marketing → Referrals. It's a marketing message, so it also needs the marketing-consent switch (Marketing → Campaigns).
| Field | Required | Description |
|---|---|---|
| phone | Yes | Customer mobile |
| name | — | Customer name |
| months | — | How many months out, 1–24 (default: the business's setting) |
→ { "ok": true, "status": "scheduled" }Otherwise `status: "skipped"` with a `reason`: not_included, disabled, no_marketing_consent, already_sent, opted_out.
Draft a review reply
POST/draft-review-response
AI drafts a reply to a given review for the OWNER to approve and post — it never writes review content itself, only responses.
| Field | Required | Description |
|---|---|---|
| review_text | Yes | The review's text |
| rating | — | Its star rating, 1–5 |
| author | — | The reviewer's name |
→ { "ok": true, "draft": "…" }Reads & searches
All reads are GETs with query parameters. List endpoints return { ok, … } objects; the four phone-keyed find/consent searches return a bare JSON array (possibly empty, never an error for "not found").
Activity stats
GET/stats
?period=today|7d|30d|this_month|last_month
Calls, answered/missed, minutes, leads (and how many were referred), appointments, outcome and sentiment breakdowns for the period — with the equal-length prior period alongside for comparison.
List calls
GET/calls
?from=&to=&outcome=&q=&topic=&limit=&offset=
Calls newest-first with outcome, sentiment, topics, rating, and a short transcript preview. q searches what was said (transcripts, AI summaries and topics — "leaking" also finds "leak"; quote a phrase for an exact match); topic filters to one AI topic tag. Paginated (limit up to 100).
Get a call
GET/calls/{id}
One call in full: complete transcript text, sentiment with reason, duration, rating, recording link.
Caller profile
GET/caller
?phone=8135551234
Everything about one number in one read: name, call history with previews, lead record (with its source and who referred them), text consent and recent messages, upcoming appointments, trusted status.
Customer timeline
GET/customer-timeline
?phone=8135551234&type=calls|texts|notes|appointments|reviews&q=&limit=
One customer's whole history, newest first: AI and team calls (summary, who answered), texts both ways, leads, appointments, review asks, callback requests, notes and emails — plus their pinned note, tags, contact details and linked job. q searches their history.
List leads
GET/leads
?from=&to=&status=hot|warm|cold&source=organic|angi|thumbtack|yelp|lsa|google_lsa|meta|digital_card|webchat|website_assistant|pbx&referred=true&limit=&offset=
Leads newest-first, paginated. `source=organic` = leads your AI captured on a call; `referred=true` = only referred leads. Rows include `source`, `lead_cost_cents`, `external_ref`, `referred_by`, and for card leads `card_id` and the intake answers.
Get a lead
GET/leads/{id}
One lead in full, including delivery history (where it was sent and whether it succeeded).
List text threads
GET/threads
?limit=
The text inbox: recent conversations with last-message previews, AI-vs-owner handling, and opt-out status.
Read a conversation
GET/thread
?phone=8135551234 (or ?id=)
One full text conversation, oldest message first. Pictures come back as `photos` — links that work for one hour.
List voicemails
GET/voicemails
?limit=
Recent voicemails with transcriptions and recording links.
Unanswered questions
GET/questions
Open questions the agent couldn't answer — the teach-loop inbox.
Account setup
GET/setup
The whole configuration in one read: business, hours, average job value and the products it owns; agent; routing and answering mode; text-back; appointment reminders; Marketing (consent, referral asks, repeat-booking); compliance posture.
Agent knowledge
GET/knowledge
What the live agent knows: knowledge base, pronunciation notes, coaching notes with statuses.
Pending follow-ups
GET/followups
Queued follow-up texts that haven't sent yet, with due times.
Usage & pacing
GET/usage
This month's AI minutes vs. the plan with an end-of-month projection, extra minutes and their cost so far (50¢/min past the allowance), plus the products owned and what the business pays per month for each.
Do-not-text registry
GET/opt-outs
Every contact who has opted out of texting.
List appointments
GET/appointments
?from=&to=&status=
Appointments soonest-first (defaults to upcoming confirmed).
Find calls
GET/find-call
?phone=8135551234
The 5 most recent calls from a phone number, with outcome, sentiment, transcript text, and recording link.
Find a contact/lead
GET/find-lead
?phone=8135551234 or ?email=maria@example.com
The 5 most recent matching leads.
Find appointments
GET/find-appointment
?phone=8135551234
Upcoming confirmed appointments for a phone number.
Check text consent
GET/consent
?phone=8135551234
Whether this number may be texted — `can_text`, `opted_out`, and whether it's a known contact. Check before texting anyone from any tool.
List reviews
GET/reviews
?rating=1-5&responded=true|false&limit=&offset=
Synced reviews newest-first — platform, rating, author, text, and whether a response has been posted.
Review stats
GET/review-stats
Average rating, review count, monthly velocity, rating distribution, needs-response count, and reviews to the next milestone.
Compliance model
Texting through this API is impossible to do non-compliantly, on purpose. Every outbound message re-checks, at the instant of send:
- Opt-outs. A customer who texted STOP is blocked everywhere until they text START. Suppressions are one-way from the API.
- Quiet hours. No automated texts outside 8am–8pm in the business's local time.
- Carrier registration. Nothing sends until the business's A2P registration is approved — unregistered traffic gets carrier-filtered and can burn the number, so we suppress it instead.
Blocked sends return 200 with status: "suppressed" and the reason, and every refusal is written to the account's append-only compliance audit trail. Use GET /consent before texting anyone from your own tools.
MCP (AI assistants)
The platform is also a remote Model Context Protocol (MCP) server, so AI assistants like Claude can work your account directly — "text my last missed caller back," "what did people call about this week?", "teach my receptionist that we now do water softeners." It exposes 52 tools: every read, search, and action on this page — activity stats, caller profiles, the text inbox, the teach-it loop, and more — with the same compliance gates as everything else.
https://crushitengine.com/api/mcp
Authenticate with your same API key. From Claude Code, for example:
claude mcp add --transport http crushitengine \ https://crushitengine.com/api/mcp \ --header "Authorization: Bearer YOUR_API_KEY"
For clients that can't send headers, append ?key=YOUR_API_KEY to the URL — but prefer the header where possible, since URLs tend to end up in logs.
Prefer Zapier?
The events and the everyday actions and searches on this page also exist as a Zapier app — instant triggers and Zap steps, no code required, with Zapier handling delivery retries. Connect it from Integrations → Automation in your dashboard using the same API key.
Questions, or need something the API doesn't cover yet? Tell us in the Help Center — integration requests get read.