The REST API
NeoKivo's public API at /v1 lets any script or platform read deals and create records. Every endpoint, real request/response examples, and how errors look.
Updated 2026-10-11
The /v1 API is a small, stable surface for reading deals, creating deals or contacts, and updating deals from outside NeoKivo. It's the same API Zapier calls under the hood, so anything Zapier can trigger on or create, a script can too.
Base URL and auth
| Setting | Value |
|---|---|
| Base URL | https://api.neokivo.com/v1 — the documented, canonical host |
| Auth header | Authorization: Bearer nk_live_... |
| Content type | application/json for POST bodies |
Create a key on the API keys page. A key acts as its creator: it sees only the deals they can see, and can only do what their workspace role allows. See that page for the fail-closed rule when a key's creator leaves the workspace.
currency, baseAmountCents and fxBaseCurrency were added to every deal shape below — purely additive, so existing integrations are unaffected. valueCents is in the deal’s own currency (its minor units — a ¥500,000 deal is 500000); baseAmountCents is that value converted to the workspace base currency, or null when no exchange rate is available yet; fxBaseCurrency names the base currency baseAmountCents is in. A won or lost deal keeps the base it was recorded in when the workspace later changes its base currency, so only add up baseAmountCents of deals with the same fxBaseCurrency.
Endpoints
| Method & path | Purpose |
|---|---|
| GET /v1/me | Connection test — returns the workspace id and name |
| GET /v1/deals | List recent deals, newest first, up to 100. Optional ?since=<ISO 8601> returns only deals created after that time |
| GET /v1/events/stage-changes | List recent stage-change events, newest first, up to 100 |
| GET /v1/actions/due | List open next actions due today or earlier — deal-anchored or contact-anchored ("keep warm") — oldest-due first, up to 100 |
| POST /v1/deals | Create a deal. Also the generic entry point other integrations use to create deals |
| PATCH /v1/deals/:id | Update a deal's title, value, stage (by name), status, or close date. A won or lost deal's value is locked (409 DEAL_CLOSED_LOCKED): reopen it first by sending status open. Reopening, or switching between won and lost, is refused while the deal has recorded payouts or a sent invoice (409 DEAL_REOPEN_BLOCKED) |
| GET /v1/calendar | List calendar events between from and to (YYYY-MM-DD, up to 93 days): deliverable due dates, usage-rights and exclusivity start and end dates, open task due dates, expected close dates, invoice due dates, contract signed and end dates, and compliance-file expiry dates. Optional scope, types and talentId |
| POST /v1/contacts | Create a contact |
Every list and create is scoped to what the key's creator can see and do. A deal private to someone else never appears, even in /v1/deals.
Examples
List deals
curl https://api.neokivo.com/v1/deals \
-H "Authorization: Bearer nk_live_YOUR_KEY"{
"deals": [
{
"id": "deal_9f2a",
"title": "Northwind annual renewal",
"valueCents": 480000,
"currency": "USD",
"baseAmountCents": 480000,
"fxBaseCurrency": "USD",
"status": "open",
"source": "referral",
"stageName": "Negotiation",
"contactName": "Dana Reyes",
"companyName": "Northwind Supply",
"createdAt": "2026-06-20T09:12:00.000Z"
}
]
}Create a deal
You send names, not ids: the stage is matched by name (or the first stage, if omitted), the company is found by name (trimmed, case-insensitive) and created if new, and the contact is matched by email or created from the name you give. Creating a new company needs the key creator to hold company.create, and creating a new contact needs contact.create; otherwise the request is rejected with 403 and nothing is written. Linking an existing company or contact needs neither. currency is optional too — omit it and the deal is created in the workspace’s base currency, same as before this field existed.
curl -X POST https://api.neokivo.com/v1/deals \
-H "Authorization: Bearer nk_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"title": "Northwind annual renewal",
"valueCents": 480000,
"currency": "USD",
"stageName": "Negotiation",
"contactName": "Dana Reyes",
"contactEmail": "dana@northwind.example",
"companyName": "Northwind Supply",
"expectedCloseDate": "2026-08-01"
}'{
"deal": {
"id": "deal_9f2a",
"title": "Northwind annual renewal",
"valueCents": 480000,
"currency": "USD",
"baseAmountCents": 480000,
"fxBaseCurrency": "USD",
"status": "open",
"source": null,
"stageName": "Negotiation",
"contactName": "Dana Reyes",
"companyName": "Northwind Supply",
"expectedCloseDate": "2026-08-01",
"createdAt": "2026-06-20T09:12:00.000Z"
}
}Update a deal
Send only the fields you want to change. stageName is matched by name, same as on create.
curl -X PATCH https://api.neokivo.com/v1/deals/deal_9f2a \
-H "Authorization: Bearer nk_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"stageName": "Negotiation",
"status": "won"
}'{
"deal": {
"id": "deal_9f2a",
"title": "Northwind annual renewal",
"valueCents": 480000,
"currency": "USD",
"baseAmountCents": 480000,
"fxBaseCurrency": "USD",
"status": "won",
"source": null,
"stageName": "Negotiation",
"contactName": "Dana Reyes",
"companyName": "Northwind Supply",
"expectedCloseDate": "2026-08-01",
"createdAt": "2026-06-20T09:12:00.000Z"
}
}Create a contact
curl -X POST https://api.neokivo.com/v1/contacts \
-H "Authorization: Bearer nk_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Dana Reyes",
"email": "dana@northwind.example",
"phone": "+1 555 0142",
"title": "VP Operations",
"companyName": "Northwind Supply",
"note": "Prefers async updates"
}'{
"contact": {
"id": "contact_4b1e",
"name": "Dana Reyes",
"email": "dana@northwind.example",
"phone": "+1 555 0142",
"title": "VP Operations",
"companyId": "company_2c7f",
"createdAt": "2026-06-20T09:12:00.000Z"
}
}List calendar events
from and to are required, inclusive dates in YYYY-MM-DD form, at most 93 days apart; anything else returns 400 with code INVALID_RANGE. scope is mine (the default: work the key creator owns, participates in or is assigned) or everyone (everything the creator may see). types is a comma-separated list of deliverable_due, usage_rights_start, usage_rights_end, exclusivity_start, exclusivity_end, task_due, deal_expected_close, invoice_due, contract_signed, contract_ends and compliance_expires; leave it out for all of them. talentId narrows to one talent. Every event is a whole day, and events never include amounts. Each kind of event follows the access rules of its own record, so campaign events need the Campaigns module and permission to view campaigns, invoice events need permission to view finance, contract dates follow access to the document, and compliance expiries need permission to view talent and their documents (or access to the deal); an event the key creator cannot see is left out, not masked.
curl "https://api.neokivo.com/v1/calendar?from=2026-11-01&to=2026-11-30&scope=everyone&types=deliverable_due,invoice_due" \
-H "Authorization: Bearer nk_live_YOUR_KEY"{
"events": [
{
"id": "deliverable_due:deliverable_71c3",
"type": "deliverable_due",
"date": "2026-11-12",
"title": "Launch reel · Mia Tanaka × Northwind Supply",
"overdue": false,
"tentative": false,
"talent": { "id": "talent_3d8e", "name": "Mia Tanaka" },
"deal": { "id": "deal_9f2a", "title": "Northwind annual renewal" },
"campaign": { "id": "campaign_5a20", "title": "Northwind autumn launch" },
"brand": { "id": "company_2c7f", "name": "Northwind Supply" },
"path": "/campaigns/campaign_5a20#deliverable-deliverable_71c3"
}
]
}Rate limits
Each API key is limited to 120 requests per minute, across the REST API and the MCP server. Past that, requests return 429 with a Retry-After header giving the seconds to wait. Back off and retry after that delay.
Errors
Every error is JSON with an error string; some also carry a code. HTTP status tells you what kind of problem it is.
| Status | Meaning |
|---|---|
| 400 | Bad input — invalid JSON, or a field failed validation (e.g. "title: String must contain at least 1 character(s)") |
| 401 | Missing, malformed, or unrecognized API key |
| 403 | The key's creator can't do this (role permission, for example setting a deal's value or currency without the finance.view permission), or the creator is gone — see API keys |
| 402 | Write blocked: the workspace is read-only (not yet activated). Body includes "code": "UPGRADE_REQUIRED". Reads still work |
| 404 | Unknown /v1 path |
| 409 | Conflict with the deal's state. "code": "DEAL_CLOSED_LOCKED": the deal is won or lost, so its value is locked until it is reopened. "code": "DEAL_REOPEN_BLOCKED": the deal cannot be reopened or change outcome because commission was paid out or an invoice was issued on it ("blockers" lists paid_out and invoiced); delete the payouts or void the invoice first |
| 429 | Rate limit exceeded: more than 120 requests in a minute on this key. Wait for Retry-After seconds |
| 500 | Unexpected server error |
{ "error": "Stage \"Discovery\" not found" }Spotted something out of date? Email hello@neokivo.com.