← Developers/MavenOSv2026-10-21
MavenOS · REST API

MavenOS API

The MavenOS API lets a script or another system read and change tasks and projects, and add or update sales companies, contacts and deals, in one workspace, as the person or service the key belongs to. Send `Authorization: Bearer mos_<prefix>_<secret>`. Every POST and PATCH needs an `Idempotency-Key` uuid. Bodies are JSON, at most 64 KB (32 KB on the Sales calls). There are no deletes, and nothing about HR is ever returned. Money is returned only as deal amounts (`one_time_cents`, `monthly_cents`), and only to a key that acts as an owner, admin or finance person. Per-key limits by rate class: basic: 60 reads and 20 writes a minute, 300 writes a day; standard: 120 reads and 30 writes a minute, 2000 writes a day; scale: 300 reads and 60 writes a minute, 10000 writes a day. Hard caps, no overage. A day is the UTC calendar day: the daily write count starts again at 00:00 UTC, and the 80 and 100 percent notices follow the same day. The per-minute limits are a rolling minute. A 429 for the daily cap sets Retry-After to the seconds left until 00:00 UTC. Writes need the API access add-on and a key with the `work:write` scope (or `crm:write` for the sales calls, which read with `crm:read`). Workspaces that require two-step sign-in refuse API keys. Browsers cannot call the API directly (no CORS); call it from a server.

Connect Claude or another MCP client to your MavenOS workspace with an Assistant key, or call REST API v2 from your own server. Either way, the connection acts as one person and sees only what that person can see.

Base URLhttps://os.mavenx.co/

Connect with MCP

MavenOS runs an MCP (Model Context Protocol) server at `/api/mcp`. It uses MCP over Streamable HTTP and is stateless: every request is a single `POST` with a JSON-RPC message, and every answer is a single JSON body. There are no sessions and no streams, and `GET` and the other methods return `405`. It supports `initialize`, `ping`, `tools/list` and `tools/call`. Batches are not supported.

The endpoint is on your MavenOS address: `https://os.mavenx.co/api/mcp`. The Assistant page in MavenOS shows the exact address for your workspace.

A connection acts as the person who made the key, with that person's own permissions. It can never do more than that person can do in MavenOS.

Make an Assistant key

Open the Assistant page in MavenOS and create a key. Give it a name, choose how long it lasts, and decide whether to turn on Allow changes. Allow changes is off by default and is not available to viewers. The key is shown once, when you create it, so copy it straight away.

A workspace owner can turn Assistant keys off for a role. When that happens, keys made by people in that role stop working.

Treat the key like a password. Anyone who has it can do whatever its person can do through the Assistant, within the limits below. Revoke it on the Assistant page if it leaks. Browser pages cannot call the endpoint, so keep the key on your own machine or server.

Add MavenOS to Claude

Send the key as a Bearer token in the `Authorization` header. In Claude Code, run this in a terminal with your own address and key:

claude mcp add --transport http mavenos https://os.mavenx.co/api/mcp \
  --header "Authorization: Bearer YOUR_ASSISTANT_KEY"

Cursor and other clients that accept a remote server with headers use the same address and header:

{
  "mcpServers": {
    "mavenos": {
      "url": "https://os.mavenx.co/api/mcp",
      "headers": { "Authorization": "Bearer YOUR_ASSISTANT_KEY" }
    }
  }
}

Claude Desktop starts remote servers through `mcp-remote`. Keep the key in an environment variable so the space in `Bearer ...` is not split:

{
  "mcpServers": {
    "mavenos": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "https://os.mavenx.co/api/mcp", "--header", "Authorization:${MAVENOS_AUTH}"],
      "env": { "MAVENOS_AUTH": "Bearer YOUR_ASSISTANT_KEY" }
    }
  }
}

To check the connection from a terminal, call `initialize`. A wrong key fails here, at connect time:

curl -X POST https://os.mavenx.co/api/mcp \
  -H "Authorization: Bearer YOUR_ASSISTANT_KEY" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-11-25"}}'

The server accepts protocol versions 2025-11-25, 2025-06-18, 2025-03-26 and 2024-11-05. Ask Claude something like "What is on my plate today?" once it is connected.

What a key can see

Reads run as the key's person under their own access rules. A member sees their own work, and Sales only if they have it. A private task, its comments and the notes about it stay hidden from everyone except the people the app shows them to. A record the person cannot see is reported as not found.

Money is shown only to owners, admins and finance. For anyone else, deal amounts come back as `null` (and the pipeline shows counts without totals), the invoice list is empty, and a request that sets deal amounts is refused.

A key cannot read HR records, pay, write-ups, reviews, incidents, contracts, private notes or free-text notes, and it cannot change HR, pay or money. It cannot reply to customers, send quotes or email, publish, or approve anything. A tool for a part of MavenOS that is switched off in your workspace is left out of the tool list.

Changes are available only when the key was created with Allow changes and its person is not a viewer or otherwise read-only. Nothing can be deleted except a project phase, and only after the person confirms. Articles are only ever created and edited as drafts. Each change is logged against the key.

Read tools

These tools only read. Lists return at most `limit` rows (1 to 50, default 15) with `has_more` and `next_cursor`; pass `next_cursor` back as `cursor` for the next page. The older short lists (marked below) take only `limit`.

ToolWhat it does
whoamiSays which workspace the key belongs to, whose access it uses and that person's role.
searchSearches clients, contacts, deals, projects, tasks, articles, expenses, support tickets and people by name or text.
get_recordReads one record found by search: client, contact, deal, project, task, article, ticket, person or expense.
list_tasksLists tasks, soonest due first. Filter by project, assignee ("me" or a person id), status, due dates and title text.
list_tasks_dueLists open tasks due within some days, or overdue, with project and assignees (short list).
list_task_commentsLists the comments on one task, oldest first.
list_projectsLists projects with status, client and dates, so you can find a project id (short list).
get_project_detailGets one project with its phases, task counts, members and links. Budgets and rates are not included.
list_project_notesLists the notes on one project, pinned first.
list_project_templatesLists the project templates, with phase and task counts.
list_clientsLists the workspace's clients by name.
list_peopleLists the people in the workspace with name and role. No pay, HR or contact records (short list).
list_companiesLists Sales companies with owner, contact count and open-deal count.
list_contactsLists Sales contacts, including do_not_email and do_not_call flags.
list_dealsLists Sales deals, most recently changed first. Amounts are null unless the person may see money.
list_open_dealsLists deals still in play, with stage and priority (short list).
pipeline_by_stageShows every pipeline stage with its deal count, and totals only for owner, admin or finance.
list_ticketsLists support tickets with status, priority, requester, assignee and deadline.
list_open_ticketsLists open, in-progress and waiting tickets, most urgent first (short list).
get_ticket_messagesReads one ticket's thread, oldest first. Attachments are not included.
list_articlesLists knowledge articles the person may read. Drafts appear only for people who can write articles.
list_content_itemsLists the content calendar for a date range. Bodies and briefs are not included.
my_inboxLists what waits on the person in Needs you: approvals, mentions, reviews, tickets and tasks. It never carries an amount.
get_my_timeShows the person's own logged time, totals and any running timer, in minutes only. Never anyone else's time, and no rates or pay.
list_time_off_pendingLists time off requests waiting for a decision that the person may see. Never the reason (short list).

Money tools

These tools read financial data. They return data only to people who can read Finance in your workspace: owners, admins and finance by default, and anyone who has been given Finance read access. For everyone else the answer is empty or says the figures are not available.

ToolWhat it does
list_invoicesAnswers "who owes me money": open Stripe invoices with amount left in cents, due date and whether each is late. Only people who can read Finance get rows.
list_expenses_to_reviewLists filed expenses waiting for approval, with vendor and amount in cents (short list).
get_safe_to_withdrawSays how much the owner can safely take out of the business, as plain lines in cents. The tax line is an estimate, not tax advice.

Write tools

These tools appear only for a key made with Allow changes. Each accepts an optional `request_id` (a UUID you make up): send the same one again and the change is made once, with the first answer returned. The person's role and the app's own rules decide each call, and none of these tools deletes anything except `delete_project_phase`.

ToolWhat it does
create_taskCreates one task, with optional description, project, phase, priority, due date, client, team, estimate and assignees. Assigning people notifies them.
update_taskChanges title, description, priority, due date or estimate on one task. Send null to clear a clearable field.
set_task_statusSets a task to open, in_progress, done or cancelled.
assign_taskAdds one person as an assignee and notifies them. It never removes anyone.
add_task_commentAdds one comment to a task, written as the key's person.
create_projectCreates one project with a name and optional client, dates and status. It adds no phases or tasks.
update_projectChanges name, client, dates or status of one project.
add_project_noteAdds one note to a project, optionally tied to a task or phase and pinned.
add_project_phaseAdds a phase at the end of a project. Not gating a phase is for owners and admins.
update_project_phaseRenames a phase or changes whether it is gated. Changing gated is for owners and admins.
delete_project_phaseDeletes one phase; its tasks are kept. Owners and admins only, and it requires confirm: true after the person agrees.
unlock_project_phaseOpens a locked phase early. Owners and admins only, and it requires confirm: true.
set_phase_tasks_statusMarks every open task in a phase done, or reopens every done task. Tasks the person cannot change are skipped.
create_project_from_templateCreates an active project from a project template, copying its phases and tasks.
create_articleCreates one knowledge article as a draft. It is never published; a person publishes it in Knowledge.
update_articleChanges fields of a draft article. Published or archived articles are refused.
create_companyAdds one Sales company. Needs Sales access.
update_companyChanges fields of one company. It cannot archive or restore a company.
create_contactAdds one Sales contact. It never changes Do Not Email or Do Not Call and sends nothing.
update_contactChanges fields of one contact. It never changes Do Not Email or Do Not Call.
create_dealCreates one Sales deal in an open stage. Setting amounts needs an owner, admin or finance person.
update_dealChanges fields of one deal other than its stage. Setting amounts needs an owner, admin or finance person.
move_deal_stageMoves a deal to another stage. Moving to won or lost, or reopening, is for owners and admins; lost needs a reason.
log_interactionRecords a call, email, meeting or other contact in Sales history. It sends nothing.
create_ticketCreates a support ticket by hand with a subject, a body and a requester. It sends no email.
add_ticket_noteAdds an internal note to a ticket. Never sent to the customer.
set_ticket_statusMoves a ticket to open, waiting_on_client, in_progress, resolved or closed.
set_ticket_prioritySets a ticket's priority to low, normal or high.
assign_ticketGives a ticket to a teammate with Support access, or removes the assignee with null.
log_timeLogs one time entry for the key's own person, as minutes or a start and end time. Weeks already submitted or approved are refused.
start_timerStarts the person's timer on a project. Only one timer can run.
stop_timerStops the running timer and logs the time. A timer under 30 seconds logs nothing.

Limits and errors

LimitValue
Reads60 calls a minute per key
Changes20 a minute and 300 a day per key
Request body16 KB
Answer sizeAbout 100,000 characters; a larger answer is replaced by a message asking for fewer rows
Page size1 to 50 rows (search takes 1 to 10 per kind)
HTTP statusMeaning
401The key is missing, malformed, revoked or expired. The response carries a `WWW-Authenticate` header.
403The key's person can no longer use Assistant keys (removed, changed role, or keys turned off for the role), the request carried a browser `Origin`, or the person needs a second sign-in step in a workspace that requires one.
405Anything other than `POST`.
413 / 415The body is over 16 KB, or the content type is not `application/json`.
400The body is not valid JSON, or it is a batch.
429Too many requests. Wait the number of seconds in `Retry-After`.
503Temporarily unavailable. Try again.

A tool that fails returns a normal JSON-RPC answer with `isError: true` and a plain sentence: access refused, not found, a part of MavenOS switched off, an invalid argument, or a change refused (not allowed, workspace read-only, or too many changes). When a change cannot be confirmed, check whether it was made before you retry, or send it again with the same `request_id`.

Using the REST API

REST API v2 lets a script or another system read and change tasks and projects, list people, and read and upsert Sales companies, contacts and deals in one workspace. Every call acts as one workspace member (the person who made the key, or a service person made for it), and that person's role decides what it can do. There are no deletes, and nothing about HR is returned. The endpoint reference on this page is generated from MavenOS's own OpenAPI description.

Paths are scoped to a workspace: `https://os.mavenx.co/api/v2/YOUR_WORKSPACE/...`. The API access page in MavenOS shows the exact address with your workspace name filled in.

export MAVENOS_API_KEY="YOUR_API_KEY"
export MAVENOS_ROOT="https://os.mavenx.co/api/v2/YOUR_WORKSPACE"

# Who is this key?
curl -H "Authorization: Bearer $MAVENOS_API_KEY" "$MAVENOS_ROOT/me"

# Create a task
curl -X POST "$MAVENOS_ROOT/tasks" \
  -H "Authorization: Bearer $MAVENOS_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"title":"Follow up with the client"}'

REST keys and scopes

API keys are separate from Assistant keys. Owners and admins create them under Settings, API access. A key looks like `mos_<prefix>_<secret>` and is shown once. Send it as `Authorization: Bearer ...`; keys in the address are not accepted. A key works only in its own workspace.

Work calls use the `work:read` and `work:write` scopes. Sales calls use `crm:read` and `crm:write`. Writes also need the API access add-on. Workspaces that require two-step sign-in refuse API keys. Deal amounts are returned and accepted only when the key acts as an owner, admin or finance person.

Call the API from a server or an automation tool. Requests that carry a browser `Origin` header are refused, and there is no CORS support.

Pagination, idempotency and rate limits

Lists answer `{ "data": [...], "next_cursor": ... }`. Use `limit` (default 50, at most 100) and pass `next_cursor` back as `after`. The cursor is opaque. Many lists also accept `updated_since`.

Every `POST` and `PATCH` needs an `Idempotency-Key` header: a UUID, or 8 to 64 letters, digits, underscores or hyphens. Repeat a call with the same key within 24 hours and nothing is written twice; you get the first answer back with `Idempotent-Replayed: true`. Reusing a key for a different call or body returns `422 idempotency_conflict`. Optionally send `MavenOS-Version: 2026-10-21`; it is the only accepted value.

Rate classReads a minuteWrites a minuteWrites a day
basic6020300
standard120302,000
scale3006010,000

Each key has one rate class. These are hard caps with no overage. The daily count resets at 00:00 UTC. Every answer carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`; a `429` carries `Retry-After` in seconds. JSON bodies are limited to 64 KB (32 KB on the Sales calls).

REST errors

Errors have the shape `{ "error": { "code": "...", "message": "...", "request_id": "..." } }`, with a `fields` list on `bad_request` that names each input and its problem (`required`, `invalid` or `unknown_field`). Quote the `request_id` when you ask for help.

StatusCodeMeaning
400bad_requestMalformed request: unknown field, value out of range, bad date, cursor or filter.
400idempotency_key_requiredA POST or PATCH had no valid Idempotency-Key.
400unsupported_versionMavenOS-Version is not 2026-10-21.
401invalid_keyMissing, wrong, revoked or expired key, or a different workspace. The answer is the same for every cause.
403forbiddenThe key or its person may not do this.
403module_offWork, People, Sales or the API access add-on is off for this workspace.
403two_factor_requiredThe workspace requires two-step sign-in, which API keys cannot provide.
403origin_not_allowedThe request came from a browser page.
403money_not_allowedA deal call sent amounts but the key's person is not an owner, admin or finance.
404not_foundNo such path or record, or a record the key cannot see.
405method_not_allowedThe path exists but not with that method. Nothing can be deleted through the API.
409workspace_read_onlyThe workspace or add-on is read-only. Reads still work.
409ambiguous_matchA Sales upsert matched more than one record. Send the external_id of the one you mean.
413payload_too_largeThe body is over the size limit.
415unsupported_media_typeSend application/json.
422idempotency_conflictThat Idempotency-Key was used for a different request.
429rate_limitedWait Retry-After seconds.
500 / 503internal / unavailableRetry with the same Idempotency-Key.

Webhooks

With the API access add-on, MavenOS can post a signed JSON message to an https address you choose when something happens. Owners and admins manage webhooks under Settings, Integrations; finance can subscribe only to the money events. Events: `lead.created`, `contact.created`, `deal.created`, `deal.stage_changed`, `deal.won`, `deal.lost`, `task.completed`, `ticket.created`, `invoice.paid` and `invoice.overdue`, plus `webhook.test` from the Send test button.

Each delivery carries `MavenOS-Event-Id`, `MavenOS-Event-Type`, `MavenOS-Delivery-Id`, `MavenOS-Timestamp` and `MavenOS-Signature` (for example `v1=...`, an HMAC-SHA256 over the timestamp and body, keyed with the signing secret shown once when you create the webhook). Only the timestamp and body are signed, so read the event id and type from the body.

Delivery is at least once, so deduplicate on the `id` in the body. A `2xx` answer within 8 seconds is success. Failed attempts are retried after 30 seconds, 2 minutes, 10 minutes, 30 minutes, 2 hours, 6 hours and 12 hours, then dropped after the eighth attempt. A `410` answer switches the webhook off at once.

The full guide, with event fields and receiver examples, is served as Markdown at `/api/v2/webhooks-guide.md`, and the event schema at `/api/v2/webhook-events.json`, both on your MavenOS address and without a sign-in.

Workspace

1 endpoint
get/api/v2/{org}/meShow this key

Returns the key's name, scopes, expiry, the person it acts as, the workspace, which tools are on, the rate limits that apply, and whether a write would be accepted right now (`can_write`). A good first call to test a key.

Parameters
optional
optional
Responses
200The record.
dataMerequired
400
401
403
405
429
500
503

Tasks

8 endpoints
get/api/v2/{org}/tasksList tasks

Tasks the key's person can see, newest change first. Private tasks appear only when that person created them, is assigned, or is an owner or admin.

Parameters
optional
optional
optional
optional
optional
optional
optional
optional
Responses
200A page of items.
dataTask[]required
next_cursorstringnullrequired

Pass as `after` for the next page. Null on the last page.

400
401
403
405
429
500
503
post/api/v2/{org}/tasksCreate a task

Creates one task as the key's person. It is public to the workspace unless `visibility` is `private`. Assigning people notifies them.

Parameters
optional
optional
optional
Request body (required)
CreateTaskBody
Responses
201Created. A repeated Idempotency-Key answers the same status with the first answer and an `Idempotent-Replayed: true` header; nothing is written again.
dataWriteItemrequired
noticeNoticeoptional
400
401
403
404
405
409
413
415
422
429
500
503
get/api/v2/{org}/tasks/{id}Get a task

One task. A task the person cannot see answers 404, the same as one that does not exist.

Parameters
optional
optional
optional
Responses
200The record.
dataTaskrequired
400
401
403
404
405
429
500
503
patch/api/v2/{org}/tasks/{id}Change a task's fields

Overwrites only the fields you send. Send null for `description`, `due_on` or `estimate_minutes` to clear it. It does not change status, assignees or who can see the task; use the status and assignees calls for those.

Parameters
optional
optional
optional
optional
Request body (required)
UpdateTaskBody
Responses
200Done. A repeated Idempotency-Key answers the same status with the first answer and an `Idempotent-Replayed: true` header; nothing is written again.
dataWriteItemrequired
noticeNoticeoptional
400
401
403
404
405
409
413
415
422
429
500
503
get/api/v2/{org}/tasks/{id}/commentsList a task's comments

Comments on one task, oldest first.

Parameters
optional
optional
optional
optional
optional
Responses
200A page of items.
dataComment[]required
next_cursorstringnullrequired

Pass as `after` for the next page. Null on the last page.

400
401
403
404
405
429
500
503
post/api/v2/{org}/tasks/{id}/commentsAdd a comment to a task

Adds one comment, written as the key's person. A comment cannot be edited or deleted through the API.

Parameters
optional
optional
optional
optional
Request body (required)
AddTaskCommentBody
Responses
201Created. A repeated Idempotency-Key answers the same status with the first answer and an `Idempotent-Replayed: true` header; nothing is written again.
dataWriteItemrequired
noticeNoticeoptional
400
401
403
404
405
409
413
415
422
429
500
503
post/api/v2/{org}/tasks/{id}/statusSet a task's status

Sets the status. Sending the status it already has changes nothing.

Parameters
optional
optional
optional
optional
Request body (required)
SetTaskStatusBody
Responses
200Done. A repeated Idempotency-Key answers the same status with the first answer and an `Idempotent-Replayed: true` header; nothing is written again.
dataWriteItemrequired
noticeNoticeoptional
400
401
403
404
405
409
413
415
422
429
500
503
post/api/v2/{org}/tasks/{id}/assigneesAdd an assignee

Adds one workspace member as an assignee and notifies them. It never removes anyone.

Parameters
optional
optional
optional
optional
Request body (required)
AssignTaskBody
Responses
200Done. A repeated Idempotency-Key answers the same status with the first answer and an `Idempotent-Replayed: true` header; nothing is written again.
dataWriteItemrequired
noticeNoticeoptional
400
401
403
404
405
409
413
415
422
429
500
503

Projects

6 endpoints
get/api/v2/{org}/projectsList projects

Projects the key's person can see, newest change first.

Parameters
optional
optional
optional
optional
optional
optional
optional
Responses
200A page of items.
dataProject[]required
next_cursorstringnullrequired

Pass as `after` for the next page. Null on the last page.

400
401
403
405
429
500
503
post/api/v2/{org}/projectsCreate a project

Creates one project as the key's person. It adds no phases or tasks.

Parameters
optional
optional
optional
Request body (required)
CreateProjectBody
Responses
201Created. A repeated Idempotency-Key answers the same status with the first answer and an `Idempotent-Replayed: true` header; nothing is written again.
dataWriteItemrequired
noticeNoticeoptional
400
401
403
404
405
409
413
415
422
429
500
503
get/api/v2/{org}/projects/{id}Get a project

One project.

Parameters
optional
optional
optional
Responses
200The record.
dataProjectrequired
400
401
403
404
405
429
500
503
patch/api/v2/{org}/projects/{id}Change a project's fields

Overwrites only the fields you send. Send null for `client_id`, `start_date` or `end_date` to clear it.

Parameters
optional
optional
optional
optional
Request body (required)
UpdateProjectBody
Responses
200Done. A repeated Idempotency-Key answers the same status with the first answer and an `Idempotent-Replayed: true` header; nothing is written again.
dataWriteItemrequired
noticeNoticeoptional
400
401
403
404
405
409
413
415
422
429
500
503
get/api/v2/{org}/projects/{id}/notesList a project's notes

Notes on one project, newest first. Notes about a task the person may not read are left out.

Parameters
optional
optional
optional
optional
optional
Responses
200A page of items.
dataNote[]required
next_cursorstringnullrequired

Pass as `after` for the next page. Null on the last page.

400
401
403
404
405
429
500
503
post/api/v2/{org}/projects/{id}/notesAdd a note to a project

Adds one note, written as the key's person, optionally tied to a task or phase of that project and pinned. A note cannot be edited or deleted through the API.

Parameters
optional
optional
optional
optional
Request body (required)
AddProjectNoteBody
Responses
201Created. A repeated Idempotency-Key answers the same status with the first answer and an `Idempotent-Replayed: true` header; nothing is written again.
dataWriteItemrequired
noticeNoticeoptional
400
401
403
404
405
409
413
415
422
429
500
503

Sales

11 endpoints
get/api/v2/{org}/crm/stagesList deal stages

The workspace's pipeline stages in board order, including archived ones, so you can see which stage keys exist. Needs a key with the `crm:read` scope. The key's person must be able to read Sales.

Parameters
optional
optional
Responses
200A page of items.
dataStage[]required
next_cursorstringnullrequired

Pass as `after` for the next page. Null on the last page.

400
401
403
405
429
500
503
get/api/v2/{org}/crm/companiesList companies

The workspace's companies, oldest change first, so a sync can run forward and never miss one: pass `updated_since` (the `updated_at` of the last record you saved; it is inclusive, so one record can repeat) and `after` (the `next_cursor` of the previous page). `updated_since` must be a real moment between 2000 and tomorrow. Needs a key with the `crm:read` scope; the key's person must be able to read Sales. `external_id` is the id your own integration sent when it created the company, or null. Companies carry no money.

Parameters
optional
optional
optional
optional
optional
Responses
200A page of items.
dataCompany[]required
next_cursorstringnullrequired

Pass as `after` for the next page. Null on the last page.

400
401
403
405
429
500
503
get/api/v2/{org}/crm/companies/{id}Get a company

One company, with the same fields as the list. A company the key's person cannot read answers 404, the same as one that does not exist.

Parameters
optional
optional
optional
Responses
200The record.
dataCompanyrequired
400
401
403
404
405
429
500
503
get/api/v2/{org}/crm/contactsList contacts

The workspace's contacts, oldest change first, paged and filtered the same way as the company list (`company_id` keeps one company's contacts). The email address is included because the contact upsert matches on it; phone numbers, notes and addresses are never returned. Contacts carry no money.

Parameters
optional
optional
optional
optional
optional
optional
Responses
200A page of items.
dataContact[]required
next_cursorstringnullrequired

Pass as `after` for the next page. Null on the last page.

400
401
403
405
429
500
503
get/api/v2/{org}/crm/contacts/{id}Get a contact

One contact, with the same fields as the list. A contact the key's person cannot read answers 404.

Parameters
optional
optional
optional
Responses
200The record.
dataContactrequired
400
401
403
404
405
429
500
503
get/api/v2/{org}/crm/dealsList deals

The deals the key's person can see (a member limited to their own deals sees only those), oldest change first, paged and filtered like the company list; `stage` keeps one stage key from GET /crm/stages. `one_time_cents` and `monthly_cents` are present only when the key acts as an owner, admin or finance person; for anyone else the two fields are left out of every deal, not set to zero. Owner and agent names are never returned.

Parameters
optional
optional
optional
optional
optional
optional
optional
Responses
200A page of items.
dataDeal[]required
next_cursorstringnullrequired

Pass as `after` for the next page. Null on the last page.

400
401
403
405
429
500
503
get/api/v2/{org}/crm/deals/{id}Get a deal

One deal, with the same fields and the same money rule as the list. A deal the key's person cannot see answers 404.

Parameters
optional
optional
optional
Responses
200The record.
dataDealrequired
400
401
403
404
405
429
500
503
post/api/v2/{org}/crm/companies/upsertCreate or update a company

Finds the company by your `external_id`, then by exact name (not case sensitive), and creates it when none matches. `on_match` says what a match does: `update` (the default) overwrites the fields you send, `fill_blanks` only fills fields that are empty, `skip` changes nothing. The answer is 201 with `match: created` for a new company and 200 with `match: updated` or `unchanged` otherwise. If two or more companies match, nothing is changed and the answer is 409 `ambiguous_match`: send the `external_id` of the one you mean. Needs the `crm:write` scope and the API access add-on. Deals and their money are separate calls.

Parameters
optional
optional
optional
Request body (required)
CompanyUpsertBody
Responses
200A record matched (`match: updated` or `unchanged`). A repeated Idempotency-Key answers the same status with the first answer and an `Idempotent-Replayed: true` header; nothing is written again.
dataUpsertResultrequired
noticeNoticeoptional
201Created: no record matched, so a new one was made (`match: created`). A repeated Idempotency-Key answers the same status with the first answer and an `Idempotent-Replayed: true` header; nothing is written again.
dataUpsertResultrequired
noticeNoticeoptional
400
401
403
404
405
409
413
415
422
429
500
503
post/api/v2/{org}/crm/contacts/upsertCreate or update a contact

Finds the contact by your `external_id`, then by email (not case sensitive), and creates it when none matches. Send an `external_id` or an `email`, or both. Link the contact to a company with `company_id` or with `company_external_id` (the `external_id` you gave when you upserted the company), not both. `on_match` works as for companies. The answer is 201 with `match: created` for a new contact and 200 with `match: updated` or `unchanged` otherwise. If two or more contacts match, nothing is changed and the answer is 409 `ambiguous_match`. Do Not Email and Do Not Call are never changed through the API. Needs the `crm:write` scope and the API access add-on. Deals and their money are separate calls.

Parameters
optional
optional
optional
Request body (required)
ContactUpsertBody
Responses
200A record matched (`match: updated` or `unchanged`). A repeated Idempotency-Key answers the same status with the first answer and an `Idempotent-Replayed: true` header; nothing is written again.
dataUpsertResultrequired
noticeNoticeoptional
201Created: no record matched, so a new one was made (`match: created`). A repeated Idempotency-Key answers the same status with the first answer and an `Idempotent-Replayed: true` header; nothing is written again.
dataUpsertResultrequired
noticeNoticeoptional
400
401
403
404
405
409
413
415
422
429
500
503
post/api/v2/{org}/crm/deals/upsertCreate or update a deal

Finds the deal by your `external_id` (required; a deal is never matched by name) and creates it when none matches. `name` and `stage` are used only when the deal is created: for a deal that already exists they are ignored, so use the stage call to move it. Link the deal to a company with `company_id` or with `company_external_id` (the `external_id` you gave the company), not both. `on_match` works as for companies. The answer is 201 with `match: created` for a new deal and 200 with `match: updated` or `unchanged` otherwise. Money: `one_time_cents` and `monthly_cents` are whole cents (integers from 0 to 1000000000000). Sending either needs a key that acts as an owner, admin or finance person; for anyone else the answer is 403 `money_not_allowed` and nothing is changed. For those people the amounts are also left out of the deal in the answer. Won, lost and not applicable go through the stage call. Needs the `crm:write` scope and the API access add-on.

Parameters
optional
optional
optional
Request body (required)
DealUpsertBody
Responses
200A record matched (`match: updated` or `unchanged`). A repeated Idempotency-Key answers the same status with the first answer and an `Idempotent-Replayed: true` header; nothing is written again.
dataDealUpsertResultrequired
noticeNoticeoptional
201Created: no record matched, so a new one was made (`match: created`). A repeated Idempotency-Key answers the same status with the first answer and an `Idempotent-Replayed: true` header; nothing is written again.
dataDealUpsertResultrequired
noticeNoticeoptional
400
401
403
404
405
409
413
415
422
429
500
503
post/api/v2/{org}/crm/deals/stageMove a deal to a stage

Moves one deal, named by its `deal_id` or by your `external_id` (send exactly one of them), to the stage with the key you give. Moving between open stages works for anyone who can write in Sales. Moving a deal to won, lost or not applicable, or reopening a closed deal, needs a key that acts as an owner or admin; for anyone else the answer is 403 `forbidden`. Moving to lost needs a `reason` (up to 500 characters). The answer is 200 with the deal as it now stands. A deal already in that stage is left as it is. A deal that does not exist, or belongs to another workspace, answers 404. Needs the `crm:write` scope and the API access add-on.

Parameters
optional
optional
optional
Request body (required)
DealStageBody
Responses
200Done. A repeated Idempotency-Key answers the same status with the first answer and an `Idempotent-Replayed: true` header; nothing is written again.
dataDealStageResultrequired
noticeNoticeoptional
400
401
403
404
405
409
413
415
422
429
500
503

People

1 endpoint
get/api/v2/{org}/peopleList people

Workspace members by name and role, so you can pick an assignee. `name` is the full name; `first_name` and `last_name` are the same name in two parts, `null` when a person has no split yet. Nothing else about a person is shown.

Parameters
optional
optional
optional
optional
Responses
200A page of items.
dataPerson[]required
next_cursorstringnullrequired

Pass as `after` for the next page. Null on the last page.

400
401
403
405
429
500
503