Yappa API
Three things you can wire up: push a lead in, get a signed webhook when Yappa books an appointment or hands a call to your team, and subscribe to bookings as a calendar feed. Everything else about an account is configured in the dashboard.
Base URL https://api.yappa.com.au. All requests and responses are JSON over HTTPS.
Authentication
One bearer token per workspace, issued by the Yappa team from your account page. Tokens start with ws_, are stored only as a hash, and can be revoked at any time. A token identifies the workspace, so you never send an account id.
Authorization: Bearer ws_live_xxxxxxxxxxxxxxxxxxxxAsk for a token at hello@yappa.com.au. Treat it as a password: it can create leads in your account. If one leaks, tell us and we revoke it on the spot.
Push a lead
POST /v1/leads puts a person into your calling queue. Use it from your CRM, your website forms, or anywhere a new enquiry lands.
POST https://api.yappa.com.au/v1/leads
Authorization: Bearer ws_live_xxxxxxxxxxxxxxxxxxxx
Content-Type: application/json
{
"phone_e164": "+61412345678", // required, E.164 only
"source": "website-form", // required, how you got them
"full_name": "Priya Nair", // optional
"email": "priya@example.com", // optional
"state_au": "VIC", // optional, see the note below
"external_id": "HS-5521", // optional, your own id
"custom_fields": { // optional, anything you like
"printer_model": "Brother MFC-L2750DW",
"quoted_price": "2400"
}
}{
"id": "3f6c1b4e-9b1a-4f2c-8d7e-1a2b3c4d5e6f",
"workspace_id": "0b7c5d6e-1111-2222-3333-444455556666",
"state": "pending",
"duplicate": false
}curl -sX POST https://api.yappa.com.au/v1/leads \
-H "Authorization: Bearer $YAPPA_TOKEN" \
-H "Content-Type: application/json" \
-d '{"phone_e164":"+61412345678","source":"website-form","full_name":"Priya Nair"}'const res = await fetch("https://api.yappa.com.au/v1/leads", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.YAPPA_TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
phone_e164: "+61412345678",
source: "website-form",
full_name: "Priya Nair",
}),
});
const lead = await res.json();
if (lead.duplicate) console.log("already on the list:", lead.id);import os, httpx
r = httpx.post(
"https://api.yappa.com.au/v1/leads",
headers={"Authorization": f"Bearer {os.environ['YAPPA_TOKEN']}"},
json={
"phone_e164": "+61412345678",
"source": "website-form",
"full_name": "Priya Nair",
},
timeout=10,
)
r.raise_for_status()
lead = r.json()Things worth knowing
- It is idempotent on the phone number. Post the same number twice and you get the first lead back with
duplicate: trueand a 201. Safe to retry; safe to replay your whole CRM. - Custom fields become things the AI can say. Any key you send is available in your call script as a placeholder, so
printer_modelcan be spoken on the call. Set them up in the dashboard under Capture fields. - A messy state is not an error.Send "nsw", "New South Wales" or nothing at all. An unrecognised value is cleaned to empty rather than rejecting the lead, and Yappa works the state out from the phone number instead.
- Pushing a lead is not permission to call it.Every dial still passes the compliance gate: Do Not Call Register, consent on file, calling hours in the lead's own state, public holidays and attempt caps. You are telling us who to call; the gate decides whether we may.
Webhooks
Yappa POSTs to your URL when something happens on a call. Configure the URL, the secret and which events you want in the dashboard under Integrations.
| Event | Fires when |
|---|---|
| booking.created | The AI booked an appointment on a call. |
| lead.transferred | The AI handed a live call to one of your people. |
X-Yappa-Event: booking.created
X-Yappa-Timestamp: 1789700000
X-Yappa-Signature: sha256=<hex digest of the raw body>
Content-Type: application/json{
"booking_id": "8a1f...",
"lead_id": "3f6c...",
"call_id": "c0de...",
"slot_start": "2026-09-22T10:30:00+10:00",
"duration_minutes": 30,
"notes_for_rep": "Wants a quote for six panels, home in Frankston."
}{
"call_id": "c0de...",
"lead_phone_e164": "+61412345678",
"lead_external_id": "HS-5521",
"rep_phone_e164": "+61400111222",
"reason": "qualified_high_intent",
"context_summary": "Wants a quote for six panels, free after 3pm.",
"conference_id": "conf_..."
}Verify the signature
The signature is an HMAC-SHA256 of the raw request body bytes, keyed with your webhook secret, hex encoded and prefixed with sha256=. Verify before you parse, and compare in constant time.
import hashlib, hmac
def valid(secret: str, raw_body: bytes, header: str) -> bool:
expected = "sha256=" + hmac.new(
secret.encode("utf-8"), raw_body, hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, header or "")import crypto from "node:crypto";
function valid(secret, rawBody, header) {
const expected =
"sha256=" + crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
const a = Buffer.from(expected);
const b = Buffer.from(header || "");
return a.length === b.length && crypto.timingSafeEqual(a, b);
}- Reply quickly. Yappa waits five seconds, then records the delivery as failed and moves on. Do your slow work after you have answered.
- Answer 2xx to acknowledge. Anything else is logged as a failure with the first part of your response body, which the Yappa team can see when you ask why a delivery did not land.
- Expect a retry to look like a duplicate. Use
booking_idorcall_idas your own idempotency key. - The timestamp is not replay protection on its own. Reject anything more than a few minutes old at your end if that matters to you, and rotate the secret if you suspect it has leaked.
Calendar feed
Every workspace has a read-only iCalendar feed of its bookings and scheduled callbacks, at a signed URL. Subscribe to it from Google Calendar, Outlook or Apple Calendar and appointments Yappa makes appear in your diary automatically.
https://api.yappa.com.au/calendar/feed/<signed-token>.icsGet the URL from the dashboard under Integrations. The token is the only credential, so treat the URL as private: anyone holding it can read your booking times. Ask us to rotate it if it gets out.
Errors
| Status | What it means |
|---|---|
| 201 | The lead was created, or already existed (check duplicate). |
| 401 | Missing, malformed, or revoked bearer token. |
| 422 | The body did not validate. The most common cause is a phone number that is not E.164. |
| 5xx | Our problem. Retry with backoff; the push is idempotent, so a retry is safe. |
Errors come back as {"detail": "..."}. Phone numbers must be E.164: a leading plus, country code, no spaces. An Australian mobile looks like +61412345678.
Need something that is not here?
Yappa also connects directly to eleven CRMs, Google Calendar and Outlook without any code, so check whether an integration already exists before you build against this. Email hello@yappa.com.au and tell us what you are trying to do.
