From a few verbs
to your first call.
Write a jambonz call flow, save it in a bin, and connect two URLs. Here is how the workspace and its API fit together.
Your first bin
- Create an account and verify your email. Follow the confirmation link before entering the editor. Check spam or use Resend verification email if needed.
- Create a bin. Give it a name and choose a starter template, or paste your own JSON array. This small example speaks a greeting and hangs up.
[
{ "verb": "say", "text": "Hello from Jambins." },
{ "verb": "hangup" }
]- Save the bin. Both JSON syntax and the jambonz schema must pass. Saving generates your content and status URLs.
- Connect jambonz. Paste Content webhook into your application's calling webhook and Status webhook into its call status webhook. Choose POST for both. Configure your phone number, speech vendors, and voice in jambonz, then make a test call.
Draft changes stay in the editor until you save. The content webhook serves the saved version. Test content checks that response; Send test event creates a synthetic callback. Neither places a phone call.
One bin, a stable home
Each account has a private collection of bins. A bin has a name, an immutable UUID, saved application JSON, an enabled state, and two independent URL secrets.
| Feature | What it does |
|---|---|
| Save & format | Publish valid JSON with two-space indentation. Ordinary edits preserve the bin ID and both secrets. |
| Starter templates | Start with a greeting, digit collection, or call forwarding. Adapt the values for your jambonz setup. |
| Search & duplicate | Find your bins by name. Duplication creates a new draft; saving it gives it a new ID and secrets. |
| Pause & enable | Pause both webhooks without deleting the bin. Both return 404 while paused; enabling restores the same URLs. |
| Rotate URL secrets | Replace both access secrets and invalidate the old URLs. Update both webhook settings in jambonz afterwards. |
| Delete bin | Permanently remove the bin and its callback history. Its webhook URLs stop working. |
Syntax and schema are separate checks
JSON syntax
Checks whether the text parses as JSON. Valid JSON alone is not enough to publish a jambonz application.
jambonz schema
Checks an array of 1–100 verbs and their parameters against @jambonz/schema 0.4.23. Errors identify the affected step.
Validation runs while drafting and again before saving. A warning flags steps after a hangup; warnings do not block a save. Schema validity does not predict call behavior, vendor availability, or the success of external action hooks. Jambins stores the application; jambonz executes it.
Callbacks, counts, and unread events
Open Status events to inspect the received payload, call ID, status, method, and timestamp. History is newest first, with 50 events per page and a button to load older events.
- The selected saved bin's count refreshes every 10 seconds, including on the Application tab.
- A small red dot marks unread events. Only events displayed in a visible Status events panel are marked read.
- Read markers survive bin switches and reloads in the same browser and website. They do not sync across devices or site domains.
- History is kept for seven days and removed by scheduled cleanup. Clear history removes it sooner.
Callbacks can contain JSON, form fields, or query fields. Common credential fields are redacted, but this is not a complete personal-data filter. Choose what your applications send. Repeated callbacks are stored separately; there is no automatic deduplication.
Account and access
- Verify before editing
- Signup requires an email, a password of 12–256 characters, and acknowledgement of the privacy and service terms. New accounts can manage their account but cannot create or view bins until verified.
- A full day signed in
- Sessions last 24 hours from sign-in, with no shorter inactivity timer. Polling does not extend that lifetime. Temporary sign-in-check failures offer a retry and preserve the draft.
- Change or recover a password
- Account settings requires your current password to set a different new password. Forgot your password? emails a one-hour, single-use link. A successful reset revokes all sessions; sign in again with the new password.
- Delete your account
- Confirm with your current password and
DELETE. Webhooks are disabled immediately, then all owned bins, callback history, and the account are removed. Cleanup waits about a minute for in-flight requests and can resume if you close the page.
Content, webhook secrets, and callback payloads are encrypted at rest. Keep a separate copy of important call flows. Jambins is offered free and as-is; see the imprint and privacy policy for service and data information.
Content & status webhooks
Copy these URLs from a saved bin. Replace the placeholders below with that bin's ID and the matching secret. The two secrets differ. No login cookie or management API headers are required.
Share it only with the intended application. A bin ID alone is not a credential. Existing URLs on jambins.functions.appwrite.awa.tools remain valid; the displayed URLs use api.jambins.com.
Content
GET / POST /b/<BIN_ID>/content?k=<CONTENT_SECRET>
Returns HTTP 200 with Content-Type: application/json and the saved jambonz array. The request payload does not change the returned flow.
curl --request POST \
'https://api.jambins.com/b/<BIN_ID>/content?k=<CONTENT_SECRET>' \
--header 'Content-Type: application/json' \
--data '{}'The response is the application array itself, without an ok or bin wrapper.
Status
GET / POST /b/<BIN_ID>/status?k=<STATUS_SECRET>
A POST accepts a JSON object with application/json or fields with application/x-www-form-urlencoded. A GET records its query fields except k. Both call_sid/call_status and callSid/callStatus are recognized.
curl --request POST \
'https://api.jambins.com/b/<BIN_ID>/status?k=<STATUS_SECRET>' \
--header 'Content-Type: application/json' \
--data '{"call_sid":"example-call","call_status":"completed"}'{ "ok": true }A 404 can mean the URL is incorrect, a secret was rotated, the bin is paused or deleted, or its owner is unavailable. The response deliberately does not reveal which condition applies. Unsupported methods return 405; a status POST with an unsupported content type returns 415. A storage failure returns 503 rather than acknowledging a lost callback.
Health
GET https://api.jambins.com/health returns {"ok":true} with HTTP 200 when backend initialization succeeds. It does not test a bin or place a call.
Management API
Base URL: https://api.jambins.com. All paths below are relative to it. Requests and responses use JSON, except successful HTTP 204 responses, which have no body. The legacy website uses its original function host for management requests.
Authentication and request headers
This is the same session-based API used by the editor. It is not an API-key or Bearer-token service. It does not allow arbitrary third-party browser origins.
- Sign in with
POST /api/login, or sign up withPOST /api/signup. These require the exact approvedOrigin,x-bin-client: web, andContent-Type: application/json. - Keep the returned host-only, Secure, HttpOnly, SameSite=Strict session cookie. Browser requests use
credentials: "include". Login/signup return acsrfvalue;GET /api/mealso returns it. - Protected writes additionally require
x-bin-csrfwith that session's CSRF value. Reads require the session cookie. Bin/history/validation operations also require editor access and enforce ownership.
Signup, login, email confirmation, and password recovery requests do not require a prior session or CSRF value. Resending verification, password changes, logout, and account deletion do. A non-browser client must supply an approved Origin and retain the cookie and CSRF token; an Appwrite project key is never needed by a Jambins client.
For the primary site, the browser supplies Origin: https://jambins.com. CORS permits only the configured Jambins origins. Do not store passwords, session cookies, CSRF values, or webhook URLs in localStorage or logs.
Bins, validation, and history
All of these endpoints require a signed-in editor. Write operations need the CSRF and client headers described above.
| Method & path | Input | Success |
|---|---|---|
GET /api/bins | — | 200 {bins:[…]} |
POST /api/bins | {name,content} | 201 {bin:…} |
GET /api/bins/<id> | — | 200 {bin:…} |
PATCH /api/bins/<id> | Any of {name,content,enabled} | 200 {bin:…} |
DELETE /api/bins/<id> | — | 204; bin and history removed |
POST /api/bins/<id>/rotate | {} | 200 {bin:…} with new URL secrets |
GET /api/bins/<id>/events | Optional ?cursor=<event-id> | 200 {events:[…],total,nextCursor} |
DELETE /api/bins/<id>/events | — | 204; history cleared |
POST /api/validate | {content} | 200 validation result |
content is a JSON string.The management API wraps the application array in a string. Use JSON.stringify(application). The content webhook returns that saved application as an array, not a quoted string.
const base = 'https://api.jambins.com';
const accountResponse = await fetch(base + '/api/me', {
credentials: 'include'
});
if (!accountResponse.ok) throw new Error('Sign in first');
const { csrf } = await accountResponse.json();
const application = [
{ verb: 'say', text: 'Hello from the API.' },
{ verb: 'hangup' }
];
const response = await fetch(base + '/api/bins', {
method: 'POST',
credentials: 'include',
headers: {
'Content-Type': 'application/json',
'x-bin-client': 'web',
'x-bin-csrf': csrf
},
body: JSON.stringify({
name: 'My greeting',
content: JSON.stringify(application)
})
});
if (!response.ok) throw new Error('Could not save the bin');
const { bin } = await response.json();
// bin.contentUrl and bin.statusUrl contain access secrets.
// Use them in jambonz; keep them out of logs.Response fields
bin contains id, name, content (string), enabled, createdAt, updatedAt, contentUrl, and statusUrl. Raw URL tokens and ownership internals are not separate response fields.
An event contains id, binId, callSid, callStatus, method, payload (object), and receivedAt. Dates use ISO 8601. Follow nextCursor for older pages until it is null; total is the bin's total event count.
{
"syntaxValid": true,
"schemaValid": true,
"issues": [],
"warnings": [],
"schemaVersion": "0.4.23"
}Validation can return HTTP 200 with schemaValid: false and readable issues. Invalid request types or oversized content are HTTP errors. Saving invalid content returns 400 and leaves the saved application intact.
Account endpoints
Public means no prior session, but the exact approved Origin and x-bin-client: web are still required. Session means a cookie; protected writes also need the CSRF token. Unverified users may use account endpoints.
| Method & path | Access & input | Success |
|---|---|---|
POST /api/signup | Public · {email,password,acceptedTerms:true} | 201 {user,csrf,verificationSent} and session cookie |
POST /api/login | Public · {email,password} | 200 {user,csrf} and session cookie |
GET /api/me | Session | 200 {user,csrf} |
POST /api/logout | Session · {} | 204; current session revoked and cookie cleared |
POST /api/verification | Session · {} | 200 {ok:true}; resend for an unverified user |
POST /api/verify | Public · {userId,secret} from the email link | 200 {ok:true} |
POST /api/password | Session · {oldPassword,password} | 200 {ok:true} |
POST /api/recovery | Public · {email} | 200 {message}; same acknowledgement for known and unknown accounts |
POST /api/recovery/confirm | Public · {userId,secret,password} | 200 {ok:true}; all sessions revoked, cookie cleared |
DELETE /api/account | Session · first {password,confirmation:"DELETE"}, then {} to continue | 202 {deleting:true} while pending; 204 when complete |
user contains account identity and state, including emailVerified, editorAccess, mustChangePassword, and deleting. A session alone does not grant editor access. While deletion is pending, only account state, logout, and deletion continuation remain available.
Verification and recovery use Appwrite Auth email, not a separate Jambins mail provider. An accepted request does not prove inbox delivery. Email-link tokens must be exchanged at the matching endpoint, not used as webhook keys or CSRF tokens.
Errors and service limits
Error bodies have the shape {"error":"Readable message"}. API and webhook responses use Cache-Control: no-store. Keep retries bounded, especially for status callbacks: retrying may create another event.
| Status | Meaning |
|---|---|
| 400 | Invalid fields, application schema, cursor, or email-link token. |
| 401 | Missing, expired, or rejected authentication. |
| 403 | Wrong origin/client/CSRF header, unverified editor access, or account restriction. |
| 404 | Missing or inaccessible bin/webhook. Bin ownership is not disclosed. |
| 405 / 415 | Unsupported webhook method / status POST content type. |
| 413 | Request, application, or callback payload is too large. |
| 429 | A rate limit was reached. Retry-After: 60 is a retry hint; longer quota windows may still apply. |
| 503 | Temporary provider, configuration, or persistence failure. A temporary account-check failure does not revoke the session. |
Size & history
- Up to 100 bins per account.
- Names: 1–80 characters.
- Applications: up to 64 KiB, 1–100 verbs.
- Callbacks: up to 64 KiB; HTTP bodies: up to 96 KiB.
- History: 50 events per page, seven-day scheduled retention.
Request rates
- Editor: 180 requests per account/minute.
- Content: 300 per bin/minute.
- Status: 120 per bin/minute and 1,000 per bin/day.
- Shared webhooks: 600 per IP/minute.
- Login/signup: 10 per IP and email/10 minutes; signup also 5 per IP/hour.
Verification exchanges and recovery confirmations are limited to 10 per IP/10 minutes. Verification resend: 3 per account/hour. Recovery requests: 5 per IP/hour and 3 per email/hour. Password changes and deletion password checks: 5 per account/10 minutes each. Deletion continuation: 40 per account/minute.
The service is intended for prototyping and modest traffic. There is no call execution engine, webhook signature check, automatic deduplication, cross-device read sync, or availability commitment.