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

  1. In Zapier, make a Zap that starts with Webhooks by Zapier → Catch Hook, and copy the address it gives you.
  2. In Current, go to Settings → API → Add webhook. Paste the address and tick only prospect.created.
  3. Press Send a test, then add a prospect in Current so Zapier sees a real one.
  4. Add the action Google Sheets → Create Spreadsheet Row and map the columns to data.prospect.name, data.prospect.phone, data.prospect.email and so on.

Get a message when a call is logged

  1. Start a Zap (or Make scenario) with a catch-hook trigger and copy its address.
  2. Add it in Settings → API as a webhook with only call.logged ticked.
  3. Add a Slack, text or email step using data.prospect.name and data.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

  1. Start a Zap (or Make scenario) with a catch-hook trigger and copy its address.
  2. Add it in Settings → API as a webhook with only prospect.updated ticked.
  3. Add a filter: continue only when data.changed_fields contains status and data.prospect.status is the stage you call won.
  4. 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.

RequestWhat it doesKey
GET /meWhich workspace the key reaches, and its access.read
GET /prospectsList prospects, oldest change first. Find one by number with ?phone=.read
GET /prospects/:idOne prospect, with its people and their numbers.read
POST /prospectsAdd a business or a person.read and write
PATCH /prospects/:idChange details. Only the fields you send change.read and write
POST /prospects/:id/outcomesLog a call: spoke, front_desk, voicemail, no_answer, not_interested, dead.read and write
POST /prospects/:id/notesAdd a note to the prospect's history.read and write
PUT /prospects/:id/follow-upSet when to call back, or clear it with null.read and write
POST /prospects/:id/do-not-contactThey asked not to be contacted. Permanent.read and write
GET /prospects/:id/activityThe prospect's history.read
GET /prospects/:id/peopleThe people at a prospect.read
POST /prospects/:id/peopleAdd a person.read and write
PATCH /people/:idChange a person's name, role or email.read and write
POST /people/:id/numbersAdd a number (mobile or landline).read and write
PATCH /numbers/:idMark a number good, wrong, disconnected or dnc; set its type; dial it first.read and write
GET /activityEvery history entry in the workspace, oldest first.read
GET /callsCalls placed from Current's dialer.read
GET /calls/:idOne call, with its transcript if it has one.read
GET /webhooksThe workspace's webhooks.read
POST /webhooksSubscribe an https address to events. Returns its secret once.read and write
POST /webhooks/:id/testSend a signed test event.read and write
POST /webhooks/:id/new-secretA new signing secret; the old one keeps signing for a day.read and write
DELETE /webhooks/:idUnsubscribe.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.createdA prospect was added: by hand, by import, by Claude or through the API.
prospect.updatedA prospect's details or status changed. changed_fields lists which.
call.loggedA call outcome was logged: spoke, front desk, voicemail, no answer, not interested or dead.
call.receivedA prospect called your Current number back: answered, missed, or left a voicemail. activity.text says which.
note.addedA note was added to a prospect's history.
follow_up.setA follow-up was set or moved.
prospect.do_not_contactA 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.