/v1 follows the rules on this page. They are applied by one shared request pipeline, so they hold for every endpoint.
Requests
- Base URL
https://api.nextlevelmca.com/v1, HTTPS only. - JSON in, JSON out. Send
Content-Type: application/jsonwith any request body. - Authenticate with
Authorization: Bearer <key or token>. See Authentication.
Response envelope
Every successful response is an object withdata and meta.
Single object
List
meta is empty for single objects, carries pagination for lists, and sometimes adds context (for example meta.deal on lender matches, meta.primary_offer_id on offers, meta.warning when a send step was skipped). Even DELETE /v1/webhooks/{id} returns an object ({ "deleted": true, "id": … }) so the envelope never changes shape. Creates return 201, everything else 200.
Errors
Every error is anerror object with the same five fields, never a bare message.
string
required
One of the seven types below. Use it to decide how to react.
string
required
Human-readable and safe to show to an operator or an AI agent.
string
Stable and machine-readable. Branch on this, not on
message.string
The request field, header or scope the error refers to, when there is one.
string
required
The same value as the
X-Request-Id response header. Quote it when contacting support.
Errors can carry extra machine-readable fields next to the standard ones;
existing_id on duplicate_business and duplicate_person (the latter also has matched_by: email or phone) is the common case.
Request ids
Every response, success or error, carriesX-Request-Id: req_…. Error bodies repeat it as error.request_id. Log it next to your own request logs; support uses it to find the server-side trace.
Pagination
List endpoints page with a cursor.meta.has_moreistruewhile there are more rows;meta.next_cursorisnullon the last page.- Keep filters identical between pages. Cursors are opaque; do not build or edit them. A cursor the server cannot read returns
400withcode: "invalid_cursor". - Lists are ordered newest first (
created_at desc, id desc) unless the endpoint says otherwise. - Some lists also report
meta.total(lender matches, submissions, deal activity).
id.
Idempotency
Send anIdempotency-Key header on any POST to make it safe to retry. Use it on everything that sends email or text messages: submissions, document requests, portal links, webhook tests.
- Keys are scoped to the credential, so two keys in the same location never collide. Maximum 255 characters; longer keys are a
400withcode: "invalid_idempotency_key". UUIDs are a good choice. - The first successful response (status and body) is stored for 24 hours. A retry with the same key, method, path and body returns the stored response with the header
Idempotent-Replayed: trueand the original status code. The action does not run again. - The same key with a different body or path is a
409withcode: "idempotency_key_reused". - While the first request is still running, a concurrent retry gets
409withcode: "idempotency_in_progress". Wait a moment and retry. - Only successful responses are stored. If the first attempt fails with a 4xx or 5xx, retrying with the same key runs the request again.
- Only
POSTreads the header.PATCH,PUTandDELETEare repeatable by nature.
Rate limits
Each credential (API key or OAuth client) is limited, per location, to:- 300 requests per minute, and
- a burst of 50 requests per second.
Exceeding either limit returns
429 with type: "rate_limit_error", code: "rate_limited" and a Retry-After header in seconds: the time to the minute reset when the per-minute limit tripped, or 1 when the burst did. Sleep for Retry-After, then retry. Do not retry in a tight loop.
Limits are not per endpoint. Webhooks are the cheap way to learn about changes; poll only for what webhooks do not cover.
Timestamps, ids and values
- Timestamps are ISO 8601 in UTC with millisecond precision, for example
2026-09-04T14:02:11.000Z. - Ids are UUIDs, as strings. CRM ids (such as
ghl_contact_id) appear as read-only fields where a record is synced. - Money is a JSON number in US dollars; percentages are numbers from 0 to 100; rates such as
factor_rateare decimals (1.32). - Phone numbers are returned as stored.
GET /v1/people?phone=accepts any format and normalises both sides to E.164 digits before matching.
Versioning
The major version is in the path:/v1. Within v1, changes are additive: new endpoints, new optional parameters, new response fields and new webhook event types can appear without notice, so ignore fields you do not recognise. Breaking changes (removing or renaming fields, changing types or semantics) only ship in a new major version, with the old one kept running through a published sunset date. Changes are listed in the changelog.