REST API reference

The base is https://app.talalevel.com/api/v1. Leads, notes, lists and deals can be read and changed; a lead's texts, calls and emails can be read; texts, emails, batches, waves and AI caller runs can be sent by a key with send. The same reference is at /openapi.json as OpenAPI 3.1.

View as Markdown

Every endpoint below is under https://app.talalevel.com/api/v1 and takes a workspace key as Authorization: Bearer tl_live_…. Requests and answers are JSON, times are ISO 8601 in UTC, and money is whole cents. Each endpoint names the permission its key needs, as area: level. The same reference is in openapi.json.

Base URL
https://app.talalevel.com/api/v1

Leads

A lead is a business or a person you are selling to, with its contacts. Its status is one of your workspace’s own statuses, and fields are your custom fields.

The lead object

  • id integer
  • name string The business, or the person when there is no business.
  • status string One of your workspace’s statuses.
  • email, city, state string or null state is two letters.
  • time_zone string or null An IANA zone, worked out from the lead’s place and numbers.
  • contacts array Each with a name, its phones (E.164, in dial order) and its emails.
  • fields object Your custom fields by name.
  • owner object or null The person on your team who owns the lead.
  • starred boolean
  • updated_at string When it last changed.
  • url string The lead’s page in the app.
The lead object
{
  "id": 123,
  "name": "Reed & Co Insurance",
  "status": "Call booked",
  "email": "marcus@reedco.example.com",
  "city": "Austin",
  "state": "TX",
  "time_zone": "America/Chicago",
  "contacts": [
    {
      "name": "Marcus Reed",
      "phones": [{ "phone": "+15125550147", "type": "mobile" }],
      "emails": []
    }
  ],
  "fields": { "Team size": "12", "Source": "Website form" },
  "owner": { "id": 4, "name": "Dana" },
  "starred": true,
  "updated_at": "2026-11-12T09:37:45.678Z",
  "url": "https://app.talalevel.com/leads/123"
}

List leads

GET/leads

Leads in the order they last changed, oldest first. Call again with next_cursor while has_more is true; once it is false, keep next_cursor and pass it on your next poll to get only what changed since. A change is listed 30 seconds after it happens, so a lead still being saved is never skipped. Needs leads: read.

Parameters

  • cursor string Where to carry on from: the last answer’s next_cursor.
  • limit integer How many to answer with. 50 when left out.
  • status string Only leads with this status, in any case.

Returns

leads, next_cursor and has_more.

Request
curl "https://app.talalevel.com/api/v1/leads?limit=50" \
  -H "Authorization: Bearer $TALALEVEL_KEY"
Response
{
  "leads": [{ "id": 123, "name": "Reed & Co Insurance", "status": "Call booked" }],
  "next_cursor": "MjAyNi0xMS0xMlQwOTozNzo0NS42Nzg…",
  "has_more": false
}

Get a lead

GET/leads/{id}

One lead. Another workspace’s lead answers 404, the same as one that does not exist. Needs leads: read.

Request
curl https://app.talalevel.com/api/v1/leads/123 \
  -H "Authorization: Bearer $TALALEVEL_KEY"
Response
{ "lead": { "id": 123, "name": "Reed & Co Insurance", "status": "Call booked" } }

Create a lead

POST/leads

Makes a lead. It needs a phone or an email. A phone already on a lead in the workspace is a 409 with that lead’s lead_id: update that lead instead, or use upsert. Needs leads: write.

Parameters

  • name string, required The business, or the person.
  • contact_name string
  • phone string A number in the United States or Canada, in any common form.
  • email string
  • status string One of your statuses, in any case. Left out, the status new leads start at. A wrong one is a 400 that names the real ones.
  • city, state string state is two letters.
  • fields object Custom fields by name, up to 50. A value is a string, number or boolean.

Returns

201 with the lead.

Request
curl -X POST https://app.talalevel.com/api/v1/leads \
  -H "Authorization: Bearer $TALALEVEL_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Reed & Co Insurance",
    "contact_name": "Marcus Reed",
    "phone": "(512) 555-0147",
    "fields": { "Source": "Website form" }
  }'
Response
{ "lead": { "id": 123, "name": "Reed & Co Insurance", "status": "New" } }

Upsert a lead

POST/leads/upsert

Finds the lead with this phone or email and updates it, or makes a new one. Use it for forms and imports so a lead is never made twice. Matching by email counts the lead’s own address and its contacts’ addresses; an email on two leads is a 409 with their lead_ids, never a guess. Matched by one, the other is added when the lead lacks it, and nothing the lead already holds is replaced. Needs leads: write.

Parameters

  • match string, required phone or email.
  • name string Required when a new lead is made.
  • Any other field of Create a lead.

Returns

200 with "created": false for a lead found, 201 with "created": true for a new one.

Request
curl -X POST https://app.talalevel.com/api/v1/leads/upsert \
  -H "Authorization: Bearer $TALALEVEL_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "match": "phone", "name": "Reed & Co Insurance", "phone": "+15125550147", "city": "Austin", "state": "TX" }'
Response
{ "lead": { "id": 123, "name": "Reed & Co Insurance" }, "created": false }

Update a lead

PATCH/leads/{id}

Changes what is given and nothing else. null clears the email, the city, the state or a field. A typed custom field refuses a value of another kind, and then nothing in the call is written. Fields for a social security number or a date of birth are refused: we never store them. A status set here shows in the lead’s history as set by the key. Needs leads: write.

Parameters

  • name, email, status, city, state string
  • fields object null for a field removes it.
Request
curl -X PATCH https://app.talalevel.com/api/v1/leads/123 \
  -H "Authorization: Bearer $TALALEVEL_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "status": "Interested", "fields": { "Team size": "14" } }'
Response
{ "lead": { "id": 123, "status": "Interested", "fields": { "Team size": "14" } } }

Notes

Notes people wrote on a lead. A note added through the API is kept exactly as written and signed by the key’s owner.

List a lead’s notes

GET/leads/{id}/notes

Newest first, with cursor and limit. Each note has id, text, at and by. Needs notes: read.

Request
curl https://app.talalevel.com/api/v1/leads/123/notes \
  -H "Authorization: Bearer $TALALEVEL_KEY"
Response
{
  "notes": [
    { "id": 881, "text": "Team of twelve, deciding by Friday.", "at": "2026-11-12T09:52:00.000Z", "by": "Dana" }
  ],
  "next_cursor": null,
  "has_more": false
}

Add a note

POST/leads/{id}/notes

Needs notes: write.

Parameters

  • text string, required Up to 10,000 characters.
Request
curl -X POST https://app.talalevel.com/api/v1/leads/123/notes \
  -H "Authorization: Bearer $TALALEVEL_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "text": "Wants the proposal by Wednesday." }'
Response
{ "note": { "id": 882, "text": "Wants the proposal by Wednesday." } }

Activity

A lead’s texts, calls and emails, newest first, with cursor and limit. Read only. Recordings and files are not given out; a call’s transcript and summary are read with the get_call tool.

List a lead’s texts

GET/leads/{id}/texts

Each text has id, direction, body, at, number (the lead’s), line (yours), state and receipt, the carrier’s word as it came. A text the carrier accepted is not proof it arrived. Needs texts: read.

Response
{
  "texts": [
    {
      "id": 9912,
      "direction": "inbound",
      "body": "This week ideally. We decide Friday",
      "at": "2026-11-12T09:14:07.000Z",
      "number": "+15125550147",
      "line": "+15125550100",
      "state": null,
      "receipt": null
    }
  ]
}

List a lead’s calls

GET/leads/{id}/calls

Each call has id, direction, at, duration in seconds, disposition, number and line. Needs calls: read.

Response
{
  "calls": [
    { "id": 4410, "direction": "outbound", "at": "2026-11-12T15:37:20.000Z", "duration": 402, "disposition": "answered", "number": "+15125550147", "line": "+15125550100" }
  ]
}

List a lead’s emails

GET/leads/{id}/emails

Each email has id, direction, at, from, to, cc, subject and body. Needs emails: read.

Lists

A saved list is a group of leads your team calls and texts from. A fixed list is exactly the leads on it. A filter list is rules, and who is on it changes as leads change.

List the lists

GET/lists

Each list has id, name, kind (fixed or filter), size (null for a filter list) and updated_at. Needs lists: read.

Response
{ "lists": [{ "id": 300, "name": "Proposal follow-ups", "kind": "fixed", "size": 4, "updated_at": "2026-11-12T21:30:00.000Z" }] }

Create a list

POST/lists

Makes a fixed list. Needs lists: write.

Parameters

  • name string, required
  • lead_ids array of integers, required

Returns

201 with the list. A lead not in the workspace is a 404 that names it in missing.

Request
curl -X POST https://app.talalevel.com/api/v1/lists \
  -H "Authorization: Bearer $TALALEVEL_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Proposal follow-ups", "lead_ids": [123, 124, 131] }'

Add leads to a list

POST/lists/{id}/leads

Puts leads on a fixed list and answers added and size. A filter list takes no leads by hand: 409. Needs lists: write.

Parameters

  • lead_ids array of integers, required

Remove leads from a list

DELETE/lists/{id}/leads

Takes leads off a fixed list and answers removed and not_here. A change that would empty the list is a 409: an empty list reads as every lead, and a dial or a batch on it would reach the whole workspace. Needs lists: write.

Parameters

  • lead_ids array of integers, required

Deals

A deal is one sale on a pipeline, at a stage whose outcome is open, won or lost. A lead can have several. Every change is checked and written into the deal’s history exactly as one made on the Pipeline page.

The deal object

  • id, lead_id integer
  • pipeline object id and name.
  • stage object id, name and outcome.
  • name string or null What is sold.
  • value_cents integer or null A monthly or yearly deal’s amount is one month’s or one year’s.
  • value_period string one_time, monthly or annual.
  • close_on, won_on, lost_on, cancelled_on date or null
  • owner, closed_by object or null
  • commission object or null Set once the deal is won.
The deal object
{
  "id": 12,
  "lead_id": 123,
  "lead_name": "Reed & Co Insurance",
  "pipeline": { "id": 3, "name": "Sales" },
  "stage": { "id": 9, "name": "Proposal", "outcome": "open" },
  "name": "Annual plan, 12 seats",
  "value_cents": 840000,
  "value_period": "annual",
  "close_on": "2026-11-30",
  "won_on": null,
  "lost_on": null,
  "cancelled_on": null,
  "owner": { "id": 4, "name": "Dana" },
  "closed_by": null,
  "commission": null,
  "updated_at": "2026-11-12T09:52:00.000Z"
}

List pipelines

GET/pipelines

Every pipeline with its stages: the ids a deal opens at and moves to. Needs pipeline: read.

Response
{
  "pipelines": [
    {
      "id": 3,
      "name": "Sales",
      "stages": [
        { "id": 7, "name": "Qualified", "outcome": "open" },
        { "id": 9, "name": "Proposal", "outcome": "open" },
        { "id": 11, "name": "Won", "outcome": "won" },
        { "id": 12, "name": "Lost", "outcome": "lost" }
      ]
    }
  ]
}

List deals

GET/deals

Deals in the order they last changed, oldest first, paged as leads are. Needs pipeline: read.

Parameters

  • cursor, limit
  • lead_id, stage_id, pipeline_id integer
  • outcome string open, won or lost.
  • changed_since string A time, such as 2026-11-11T09:00:00Z.

Get a deal

GET/deals/{id}

Needs pipeline: read.

Open a deal

POST/deals

Opens a deal on a lead, on the workspace’s pipeline at its first open stage unless you say otherwise. It is the key owner’s unless owner_id names someone else. Needs pipeline: write.

Parameters

  • lead_id integer, required
  • pipeline_id, stage_id integer A stage in progress.
  • name string What is sold.
  • value_cents integer
  • value_period string one_time, monthly or annual.
  • close_on, renews_on date
  • owner_id, term_months integer
Request
curl -X POST https://app.talalevel.com/api/v1/deals \
  -H "Authorization: Bearer $TALALEVEL_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "lead_id": 123, "name": "Annual plan, 12 seats", "value_cents": 840000, "value_period": "annual", "close_on": "2026-11-30" }'
Response
{ "deal": { "id": 12, "lead_id": 123, "stage": { "id": 7, "name": "Qualified", "outcome": "open" } } }

Update a deal

PATCH/deals/{id}

Changes a deal’s facts: any field of Open a deal but lead_id and the stage. null clears. A changed amount on a won deal changes its commission with it. An empty body is a 400. Needs pipeline: write.

Move a deal

POST/deals/{id}/move

Moves a deal to another stage of its pipeline. Moving to a won stage is the sale: it needs an amount, here or already on the deal. Moving to a lost stage, the stage is the reason. Moving back to an open stage reopens it, unless its commission was already marked paid (409). Needs pipeline: write.

Parameters

  • stage_id integer, required
  • won_on date For a won stage. Today where the lead is, when left out.
  • closed_by integer A person on the team. The deal’s owner, when left out.
  • rep_bps integer The closer’s share in hundredths of a percent. The workspace’s share, when left out.
Request
curl -X POST https://app.talalevel.com/api/v1/deals/12/move \
  -H "Authorization: Bearer $TALALEVEL_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "stage_id": 11 }'
Response
{ "deal": { "id": 12, "stage": { "id": 11, "name": "Won", "outcome": "won" }, "won_on": "2026-11-12" } }

Cancel a sale

POST/deals/{id}/cancel

Marks a won sale as cancelled by the customer, or with null, undoes that. Whether the commission is taken back is the workspace’s own rule. Needs pipeline: write.

Parameters

  • on date or null, required The day the customer cancelled.

Texts and emails

Sending needs the area at send. A send goes through every check a send in the app goes through: opt-outs, quiet hours in the lead’s time zone, message length and your numbers’ registration. Where a person in the app would see a warning, the API refuses with a 422 and the reason, and nothing is sent. Texts and emails sent with a key count against its daily limit, 20 in 24 hours unless the owner changes it.

Send a text

POST/texts

Texts one of the lead’s own numbers from your registered numbers. Never a number typed in. Needs texts: send.

Parameters

  • lead_id integer The lead. Or conversation_id to answer in one thread.
  • text string, required Up to three segments.
  • number string One of the lead’s own numbers, when not the first.

Returns

201 with state, messageId, conversationId, to, from and segments.

Request
curl -X POST https://app.talalevel.com/api/v1/texts \
  -H "Authorization: Bearer $TALALEVEL_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "lead_id": 123, "text": "Hi Marcus, Dana here. Does 9:30 tomorrow still work?" }'
Response
{
  "state": "sent",
  "messageId": 20417,
  "conversationId": 5521,
  "to": "+15125550147",
  "from": "+15125550100",
  "segments": 1
}

Send an email

POST/emails

Emails one of the lead’s own addresses, from the key owner’s Gmail or one of the workspace’s addresses. Needs emails: send.

Parameters

  • lead_id integer, required
  • subject string, required
  • body string, required Plain text, a blank line between paragraphs.
  • to string One of the lead’s own addresses, when not the first.
  • from string One of your sending addresses, when not the default.

Returns

201 with id, from and to.

Batches and waves

A batch sends one template to a list. A wave texts a list in several touches on a schedule. Each is a preview, then a start with the preview’s id. A preview changes nothing; it is good once, for 15 minutes, for the key that made it. The start checks everything again and is refused as changed if anything moved since.

List templates

GET/templates

The text templates that are on, each with id, name and a sample. Needs texts: read.

Preview a batch

POST/batches/preview

Says how many texts would go, what they cost, who is left out and why, and what waits for the lead’s morning. previewId is null when nothing can go. Needs texts: send.

Parameters

  • name string, required The batch’s name on the Texts screen.
  • template_id integer, required
  • list_id integer Or lead_ids.
  • template_b_id integer A second template, for an A/B.
  • split integer With two templates, the first one’s share in percent.
  • max_rows integer Send to at most this many.
Request
curl -X POST https://app.talalevel.com/api/v1/batches/preview \
  -H "Authorization: Bearer $TALALEVEL_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "November follow-up", "template_id": 31, "list_id": 300 }'

Start a batch

POST/batches

Starts a previewed batch. It waits a minute before it sends; cancelBy is the end of that minute. Needs texts: send.

Parameters

  • preview_id string, required

Returns

201 with batchId, cancelBy and texts.

Request
curl -X POST https://app.talalevel.com/api/v1/batches \
  -H "Authorization: Bearer $TALALEVEL_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "preview_id": "<previewId from the preview>" }'

Cancel a batch

POST/batches/{id}/cancel

Within its first minute nothing goes. After that, texts not yet handed to the carrier stay unsent. Needs texts: send.

List waves

GET/waves

Needs texts: read.

Preview a wave

POST/waves/{id}/preview

Answers previewId and the wave’s plan. Needs texts: send.

Run a wave

POST/waves/run

Runs a previewed wave now and answers waveId and name. Needs texts: send.

Parameters

  • preview_id string, required

AI caller

Joey, the AI caller, calls a list for you, one run at a time in a workspace, inside its calling hours. On the Scale plan.

Get the AI caller

GET/ai-runs

The run now and today’s calls. Needs calls: read.

Preview a run

POST/ai-runs/preview

Answers previewId and the leads it would call. Needs calls: send.

Parameters

  • list_id integer Or lead_ids.

Start a run

POST/ai-runs

Needs calls: send.

Parameters

  • preview_id string, required
  • calls_per_day integer

Returns

201 with runId and queued.

Stop the AI caller

POST/ai-runs/stop

No new calls start. A call already on carries on to its end. Needs calls: send.

Resume a run

POST/ai-runs/{id}/resume

Needs calls: send.

Tools

Everything the MCP server can do is also a REST call, by the tool’s name. Use it for what has no route of its own: searching calls and transcripts, tasks, the calendar, activity figures, drafts. This is what the command line uses.

List tools

GET/tools

The tools this key may run now, each with name, title, description, area, level, changes, sends and input as JSON Schema.

Run a tool

POST/tools/{name}

The body is the tool’s input. Answers text, written for a model to read, and data with the same as fields where the tool has them. A refusal is a 422 with the reason, a tool the key may not run a 403, and a name that does not exist a 404.

Request
curl -X POST https://app.talalevel.com/api/v1/tools/search_calls \
  -H "Authorization: Bearer $TALALEVEL_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "reached": "human", "from": "2026-11-10", "limit": 5 }'
Response
{
  "text": "call 4410 · Nov 12, 9:37 AM · lead 123 Reed & Co Insurance · outbound · 6m42s · human · Dana\n<<<summary\nTeam of twelve, deciding by Friday. Wants the proposal by Wednesday.\n>>>"
}

Not in the API

  • Placing a call as a person. Calls are made in the app’s dialer.
  • Deleting a lead or a deal.
  • Recordings and files.
  • Marking a commission paid.

Questions

Can I send a text message through the TalaLevel API?
Yes. POST /texts with a lead_id and the text, using a key that has texts at send. It goes from your own registered numbers and through the same checks as a text sent in the app.
How do I avoid duplicate leads when importing?
Use POST /leads/upsert with match set to phone or email. It updates the lead that has that phone or email, or makes a new one.
Can the API start the AI caller?
Yes. POST /ai-runs/preview with a list, then POST /ai-runs with the preview's id, using a key that has calls at send.

© 2026 TalaLevel · talalevel.com · openapi.json · llms.txt · Privacy · Terms