Browse documentation

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

SettingValue
Base URLhttps://api.neokivo.com/v1 — the documented, canonical host
Auth headerAuthorization: Bearer nk_live_...
Content typeapplication/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 & pathPurpose
GET /v1/meConnection test — returns the workspace id and name
GET /v1/dealsList recent deals, newest first, up to 100. Optional ?since=<ISO 8601> returns only deals created after that time
GET /v1/events/stage-changesList recent stage-change events, newest first, up to 100
GET /v1/actions/dueList open next actions due today or earlier — deal-anchored or contact-anchored ("keep warm") — oldest-due first, up to 100
POST /v1/dealsCreate a deal. Also the generic entry point other integrations use to create deals
PATCH /v1/deals/:idUpdate 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/calendarList 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/contactsCreate 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

bash
curl https://api.neokivo.com/v1/deals \
  -H "Authorization: Bearer nk_live_YOUR_KEY"
json
{
  "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"
    }
  ]
}
The list response omits expectedCloseDate — it's a lighter shape than the single-deal response below.

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.

bash
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"
  }'
json
{
  "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"
  }
}
201 Created.

Update a deal

Send only the fields you want to change. stageName is matched by name, same as on create.

bash
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"
  }'
json
{
  "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"
  }
}
200 OK.

Create a contact

bash
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"
  }'
json
{
  "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"
  }
}
201 Created. note is saved on the contact but not echoed back in the response.

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.

bash
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"
json
{
  "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"
    }
  ]
}
Events are sorted by date. overdue is true for an open item past its date. tentative is true for work on a provisional campaign (its deal is not yet won): it is planned but not committed, and is never overdue. path is the app route of the record, and the talent, deal, campaign and brand references are null when an event has none.

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.

StatusMeaning
400Bad input — invalid JSON, or a field failed validation (e.g. "title: String must contain at least 1 character(s)")
401Missing, malformed, or unrecognized API key
403The 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
402Write blocked: the workspace is read-only (not yet activated). Body includes "code": "UPGRADE_REQUIRED". Reads still work
404Unknown /v1 path
409Conflict 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
429Rate limit exceeded: more than 120 requests in a minute on this key. Wait for Retry-After seconds
500Unexpected server error
json
{ "error": "Stage \"Discovery\" not found" }
A validation error from POST /v1/deals with an unknown stage name.

Spotted something out of date? Email hello@neokivo.com.