API reference · v1
Build on Current
Read and update your prospects, people, calls and history from your own code, and get told the moment something happens. The API is free on every workspace.
Getting started
In Current, open Settings → API (owners and admins) and make a key. Then check it works:
curl https://currentcrm.io/api/v1/me \
-H "Authorization: Bearer cur_live_..."{
"workspace": { "id": "...", "name": "Acme Calling" },
"key": { "name": "Reporting sheet", "prefix": "cur_live_Ab3x", "access": "read" },
"acting_as": { "email": "you@example.com" }
}Recipes
Step-by-step versions of what people build most. The no-code ones use a webhook tool you may already have: Zapier's "Webhooks by Zapier" (on its paid plans) or Make's Webhooks module. Both give you an address to paste into Current, and both can send API requests.
Send new prospects to a spreadsheet
- In Zapier, make a Zap that starts with Webhooks by Zapier → Catch Hook, and copy the address it gives you.
- In Current, go to Settings → API → Add webhook. Paste the address and tick only
prospect.created. - Press Send a test, then add a prospect in Current so Zapier sees a real one.
- Add the action Google Sheets → Create Spreadsheet Row and map the columns to
data.prospect.name,data.prospect.phone,data.prospect.emailand so on.
Get a message when a call is logged
- Start a Zap (or Make scenario) with a catch-hook trigger and copy its address.
- Add it in Settings → API as a webhook with only
call.loggedticked. - Add a Slack, text or email step using
data.prospect.nameanddata.activity.text("Had conversation", "Left voicemail", "No answer").
Log calls from another dialer
When your dialer finishes a call, find the prospect by the number dialled, then log the outcome. You'll need a read-and-write key.
# 1. Find the prospect by phone (any format works)
curl "https://currentcrm.io/api/v1/prospects?phone=6025550147" \
-H "Authorization: Bearer cur_live_..."
# 2. Log how it went, with the id from step 1
curl -X POST https://currentcrm.io/api/v1/prospects/PROSPECT_ID/outcomes \
-H "Authorization: Bearer cur_live_..." \
-H "Content-Type: application/json" \
-d '{ "outcome": "voicemail" }'Outcomes are spoke, front_desk, voicemail, no_answer, not_interested and dead, exactly like the buttons in Current. With front_desk you can also send ask_for ({ name, role }) and front_desk_name: the person who decides becomes who to ask for.
Add prospects from a form
Send each form submission to POST /prospects. Put the submission's own id in an Idempotency-Key header, so if the form tool sends it twice, Current adds it once.
curl -X POST https://currentcrm.io/api/v1/prospects \
-H "Authorization: Bearer cur_live_..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: form-submission-81723" \
-d '{ "type": "person", "name": "Maria Delgado", "phone": "(480) 555-0199",
"address": { "city": "Tempe", "state": "AZ" }, "source": "Website form" }'Want to call them while they're still on your site? Switch on speed to lead in Settings → In-app calling, and each one rings your cell as it arrives, saying who it is and where it came from (the source). Press 1 to call them. It rings the cell of the person who made the key.
Start the next step when a deal is won
- Start a Zap (or Make scenario) with a catch-hook trigger and copy its address.
- Add it in Settings → API as a webhook with only
prospect.updatedticked. - Add a filter: continue only when
data.changed_fieldscontainsstatusanddata.prospect.statusis the stage you call won. - Add the step that comes next for you: an invoice, a project, a calendar invite, a welcome email.
A weekly report of your calls
Ask for everything that happened since a date. Each entry's type says what it was (a call outcome, a note, an email), and prospect_id says who it was with. A read-only key is enough.
curl "https://currentcrm.io/api/v1/activity?since=2026-10-05T00:00:00Z&limit=100" \
-H "Authorization: Bearer cur_live_..."If the answer has a next_cursor, ask again with cursor= set to it until it's empty. From a Google Sheet, Apps Script's UrlFetchApp.fetch can make this request on a weekly timer.
Keys
Send your key in the Authorization header on every request. Each key reaches one workspace and acts as the person who made it, so what it writes shows in history as theirs, marked api.
A key is read only or read and write. It's shown once when it's made; Current stores only a hash. Treat it like a password: keep it on your server, never in a web page or an app people download. Only a workspace's owner or admins can make keys, and a key stops working straight away if it's revoked, or if the person who made it stops being an admin or leaves (they and the workspace's admins are emailed when that happens).
Endpoints
All paths are under https://currentcrm.io/api/v1. Requests and responses are JSON.
| Request | What it does | Key |
|---|---|---|
| GET /me | Which workspace the key reaches, and its access. | read |
| GET /prospects | List prospects, oldest change first. Find one by number with ?phone=. | read |
| GET /prospects/:id | One prospect, with its people and their numbers. | read |
| POST /prospects | Add a business or a person. | read and write |
| PATCH /prospects/:id | Change details. Only the fields you send change. | read and write |
| POST /prospects/:id/outcomes | Log a call: spoke, front_desk, voicemail, no_answer, not_interested, dead. | read and write |
| POST /prospects/:id/notes | Add a note to the prospect's history. | read and write |
| PUT /prospects/:id/follow-up | Set when to call back, or clear it with null. | read and write |
| POST /prospects/:id/do-not-contact | They asked not to be contacted. Permanent. | read and write |
| GET /prospects/:id/activity | The prospect's history. | read |
| GET /prospects/:id/people | The people at a prospect. | read |
| POST /prospects/:id/people | Add a person. | read and write |
| PATCH /people/:id | Change a person's name, role or email. | read and write |
| POST /people/:id/numbers | Add a number (mobile or landline). | read and write |
| PATCH /numbers/:id | Mark a number good, wrong, disconnected or dnc; set its type; dial it first. | read and write |
| GET /activity | Every history entry in the workspace, oldest first. | read |
| GET /calls | Calls placed from Current's dialer. | read |
| GET /calls/:id | One call, with its transcript if it has one. | read |
| GET /webhooks | The workspace's webhooks. | read |
| POST /webhooks | Subscribe an https address to events. Returns its secret once. | read and write |
| POST /webhooks/:id/test | Send a signed test event. | read and write |
| POST /webhooks/:id/new-secret | A new signing secret; the old one keeps signing for a day. | read and write |
| DELETE /webhooks/:id | Unsubscribe. | read and write |
Prospects
A prospect is a business (with people under it) or a person (someone called at home, with an address and several numbers). Adding one:
curl -X POST https://currentcrm.io/api/v1/prospects \
-H "Authorization: Bearer cur_live_..." \
-H "Content-Type: application/json" \
-d '{
"type": "business",
"name": "Desert Air Cooling",
"phone": "(602) 555-0147",
"contact": { "name": "Pat Rivera", "role": "Owner" },
"address": { "city": "Mesa", "state": "AZ" }
}'A duplicate (the same phone, or the same company in the same area) is refused with 409 and the existing prospect's id in error.details.existing_id. So is anyone on the do-not-contact list. Logging a call:
curl -X POST https://currentcrm.io/api/v1/prospects/PROSPECT_ID/outcomes \
-H "Authorization: Bearer cur_live_..." \
-H "Content-Type: application/json" \
-d '{ "outcome": "spoke", "note": "Call back after the 15th",
"follow_up_at": "2026-10-16T16:00:00Z", "follow_up_for": "send pricing" }'Times are ISO 8601 with a time zone (Z for UTC). A person's numbers each have a status; onlygood ones are dialled, and a prospect's phone is always its first good number.
Lists and paging
Lists return up to limit items (default 50, at most 200), oldest first, and a next_cursor. Pass it back as cursor for the next page; null means that was the last. Prospects are ordered by when they last changed, so to keep a copy in sync, save the last cursor and ask again later, or use updated_since.
GET /prospects?limit=100&updated_since=2026-10-01T00:00:00Z
→ { "data": [ ... ], "next_cursor": "eyJ0Ijoi..." }Errors
Every error has the same shape. code is stable for your code to check; message is for people.
{ "error": { "code": "invalid_request", "message": "Some fields need fixing.",
"details": { "fields": [ { "field": "name", "problem": "Required" } ] } } }Codes: invalid_request 400, unauthorized 401, forbidden 403 (a read-only key writing),not_found 404, conflict 409, limit_reached 409 (the workspace's plan limit),rate_limited 429, server_error 500.
Rate limits
Each key can make 120 requests a minute. Every response says where you are inX-RateLimit-Remaining and X-RateLimit-Reset; going over returns 429 withRetry-After in seconds.
Safe retries
Send an Idempotency-Key header (any unique string, like a UUID) with a write. If you retry the same request with the same key, for example after a timeout, you get the first answer back instead of a second prospect. Reusing a key for a different request returns 409. Keys are remembered for 24 hours.
Webhooks
A webhook is an https address Current calls the moment something happens. Add one in Settings → API or withPOST /webhooks, choosing events (or none, for all of them):
| prospect.created | A prospect was added: by hand, by import, by Claude or through the API. |
| prospect.updated | A prospect's details or status changed. changed_fields lists which. |
| call.logged | A call outcome was logged: spoke, front desk, voicemail, no answer, not interested or dead. |
| call.received | A prospect called your Current number back: answered, missed, or left a voicemail. activity.text says which. |
| note.added | A note was added to a prospect's history. |
| follow_up.set | A follow-up was set or moved. |
| prospect.do_not_contact | A prospect asked not to be contacted. Permanent. |
Each delivery is a POST with a JSON body like this:
{
"id": "evt_8f3c...", // the same for every endpoint; use it to ignore repeats
"type": "call.logged",
"created_at": "2026-10-13T18:04:00.000Z",
"workspace_id": "...",
"data": { "prospect": { ... }, "activity": { "type": "call", "text": "Had conversation", ... } }
}Answer with any 2xx within 10 seconds. Anything else is retried after about 1, 2, 6, 12 and 24 hours (retries go out on the hour, between 13:00 and 05:00 UTC); an endpoint whose last 5 deliveries all fail is switched off until you switch it back on. Deliveries can arrive more than once or out of order, so check id.
Checking a delivery is really from Current
Each delivery has a Current-Signature header, t=TIMESTAMP,v1=SIGNATURE, where the signature is an HMAC-SHA256 of TIMESTAMP.RAW_BODY using your endpoint's secret. For a day after you get a new secret there are two v1 values, one per secret; a delivery is genuine if either matches. In Node:
import { createHmac, timingSafeEqual } from "node:crypto";
function isFromCurrent(rawBody, header, secret) {
const pairs = header.split(",").map((part) => part.split("="));
const t = pairs.find(([key]) => key === "t")?.[1];
if (!t || Math.abs(Date.now() / 1000 - Number(t)) > 300) return false; // missing, or older than 5 minutes
const expected = createHmac("sha256", secret).update(`${t}.${rawBody}`).digest();
return pairs
.filter(([key]) => key === "v1")
.some(([, sig]) => {
const given = Buffer.from(sig ?? "", "hex");
return given.length === expected.length && timingSafeEqual(given, expected);
});
}Use the body exactly as received, before parsing it. Webhooks only go to public https addresses on port 443.
Replacing keys and secrets
To change a key without breaking anything, open its menu in Settings → API and choose Replace with a new key. You get a new key with the same name and access, and the old one keeps working for 24 hours while you put the new one in your program. If a key leaked, choose Revoke instead: it stops at once.
A webhook's secret works the same way: Get a new secret keeps the address, events and delivery history, and signs deliveries with both secrets for 24 hours.
Rules and versioning
The API follows the same rules as the app: nobody on the do-not-contact list is called or re-added, and each workspace's plan limits apply. Version 1 can't delete records, place calls, or send email or texts.
Within v1, fields and endpoints are only ever added, never renamed or removed. Every request is recorded in the workspace's request log (Settings → API) without its contents. See also Security. Questions or a problem? hello@obramade.com.