Partner API
The Partner API gives partner users a company-scoped way to work with the
Journeybee data shared with them. The partner surface is separate from the
admin API and follows the same room, partnership, and sharing settings as the
partner portal.
Base URL
Authentication
Partner connections use either OAuth or a partner API key. Send the credential
in the Authorization header:
OAuth (authorization-code + PKCE) is how interactive MCP clients — Claude,
ChatGPT, Cursor, and similar — connect to your partnership data. Discovery and
client registration happen automatically via the MCP server, so there’s
nothing to configure by hand; see Partner MCP for the
connect flow. For custom REST integrations, use a partner API key instead.
Partner API keys are issued in the partner portal and are shown only once.
Store them securely and send them as bearer credentials.
Getting started
Call GET /v1/partner/me with your credential to find out who you are and
which partnerships you can act on:
The response includes your user identity, your read/write permissions, and
the partnerships available to you — each with a uuid, name,
partner_type, and enabled. That uuid is the {partnershipId} you send on
every other request (see Company and partnership scope
below).
Examples
List deals for a partnership:
Create a lead:
API keys
Partner API keys are created and revoked on the portal’s API & MCP page.
When you create one you choose read and/or write access — the same
scopes an OAuth connection carries — and the full key is shown exactly once,
so store it securely. A key is scoped to your partner user and the company
whose portal you created it on (not to a single partnership), and starts with
the jb_partner_ prefix. Each partner user may have up to 10 active keys per
company — revoke ones you no longer need before creating more.
Scopes and permissions
Connections can have read and write access. Most partner operations are
read-only; write endpoints require the connection to have write access.
Grant only the permissions an integration needs.
Company and partnership scope
Credentials are scoped to a partner user and company. A partner can have
connections for multiple companies, and each company must be authorized
separately.
The partnership is selected for each API request:
The API validates that the authenticated partner is an active member of the
requested partnership at the credential’s company. A credential never grants
access to partnerships at another company.
IDs are UUIDs
Every id in a partner API request or response is a UUID — partnership, lead,
deal, tag, user, and contact ids are all uuid strings. IDs you receive back
from a request round-trip directly into filters and update calls; the API
never exposes numeric ids as resource identifiers. A few numeric fields do
appear as display-only metadata (logo/asset ids, custom-field option ids
inside options) — these are never accepted as identifiers in a request.
Responses & errors
List endpoints return a paginated envelope:
Errors follow a single envelope:
code is one of unauthorized, forbidden, module_disabled, not_found,
validation_error, conflict, rate_limit_exceeded, or internal_error.
module_disabled means the company hasn’t enabled the Partner API for its
portal.
Requests are rate-limited to 500 requests per minute per partner user —
each partner user has their own budget, separate from other users at the same
company. Exceeding it returns a 429 Too Many Requests response with a
Retry-After header.
Field requirements
Custom fields on leads and deals can be marked required, or controlled by a
custom field rule (auto-populating, hiding, or locking another field). Before
creating or updating a lead or deal, call the matching requirements endpoint
to learn the constraints your submission must satisfy:
The response lists the custom fields available to this partnership — with
each field’s required flag — and the active rules that apply to them:
required applies only while a field is visible — a required field hidden
by one of the listed rules must not be supplied (submitting it returns
hidden_by_rule); it becomes required once its rule condition makes it
visible. In the example above, “Renewal Terms” is required, but only while
the “Show renewal terms for large Enterprise deals” rule keeps it visible.
A rule’s primary condition is source_field with source_values. A condition
matches when the field’s value matches any of its values, and a number field
can use a threshold such as { "operator": ">", "value": 100 }. A rule can
also carry extra conditions, each with its own field and values, and a
condition_operator:
- For
show_field and set_value, the primary condition and every extra
condition are combined with condition_operator: and needs all of them
to match, or needs any one. The example rule shows “Renewal Terms” only
for an Enterprise deal with more than 100 rooms.
- For
value_mapping, the extra conditions are a gate. They are combined
among themselves with condition_operator, and the first matching
value_mappings row on source_field applies only while the gate passes.
With no extra conditions, the gate always passes.
conditions is an empty array when a rule has only its primary condition, so
a client that evaluates rules itself must check every entry, not just
source_field.
Deal field requirements follow the deal’s owning partnership; when creating a
deal from another partnership’s lead (a distributor roll-up), the lead’s own
partnership type governs instead.
Only fields and rules you’re actually allowed to read/write on this surface
are included. A rule is left out entirely when its target field or any of its
condition fields (the source field or any field in conditions) isn’t visible
to a partner, since it can never be satisfied. Both endpoints require
the same room access (leads/deals) as the corresponding list/create
endpoints.
If a create or update call violates one of these constraints, it returns
400 validation_error with a details array — one entry per rejected field,
each with the field’s id (uuid), a machine-readable reason (e.g.
missing_required, hidden_by_rule, locked_by_rule), and a human-readable
message. See Custom field validation
for the full reason code reference.
The standard deal name follows the same rule. When the vendor hides the deal
name, POST /v1/partner/{partnershipId}/deals rejects a non-empty
deal_label and PATCH /v1/partner/{partnershipId}/deals/{dealId} rejects a
non-empty fields.label. Either returns 400 validation_error with a
details entry whose field is deal_label (create) or fields.label (update) and
whose reason is hidden_by_rule; a rejected update applies none of its other
fields. Omit the label instead: a created deal is then named after the lead’s
company name, and an existing deal keeps its current name.
A vendor can attach a form to two lead statuses: the Accepted form (status
converted) and the Rejected form (status rejected). The leads requirements
endpoint returns both under status_forms (the deals endpoint does not):
mode is off (no form), optional (the fields and reason may be sent),
or required (every required field and a required reason must be sent).
A field’s required flag only binds while the mode is required.
fields lists only the form’s fields the vendor shares with partners.
reason is the form’s built-in reason: accepted_reason for the Accepted
form, rejected_reason for the Rejected form. It is null when the vendor
hides that reason from partners. required is true only when the mode is
required and the vendor requires the reason.
Send the form in the same updatePartnerLead request that changes the
status: answers in custom_field_values by field uuid, and the reason in
accepted_reason or rejected_reason:
The updated lead comes back with the reason and the current form’s answers:
(Other lead fields are omitted above.) rejected_reason and
accepted_reason are set only while the lead is in their status and the
vendor shows them to partners; otherwise they are null. status_form_fields
holds the shared answers of the current status’s form, and is empty while the
lead is in any other status.
The request is refused, and nothing in it is saved, when:
- the form is
required and a required field or a required reason is
missing. This returns 400 validation_error with a missing_required
detail for each one (field is the field’s uuid, or accepted_reason /
rejected_reason).
- a form answer or a reason is sent without moving the lead into that form’s
status, or a reason is sent while the vendor hides it. This returns
400 validation_error with a not_accessible detail. Sending back the
reason value you can already read for the lead (null when hidden or not in
that status) is not a change and is accepted.
- the partnership has lead status locked and the request changes the status
or a reason. See Partnership locks.
Answers sent for the form of the status the lead is leaving are ignored, not
refused, because leaving that status clears the form’s shared answers.
rejected_reason and accepted_reason are limited to 2000 characters. When
the lead later leaves Accepted or Rejected, that form’s shared answers and its
reason (when you can see it) are cleared.
Visibility
Partner API responses follow the same visibility rules as the partner portal.
Room settings, partnership settings, sharing settings, campaign sharing, and
ownership rules can affect which records are visible or writable.
The leads and deals endpoints specifically require the company to expose the
matching room (leads or deals) to that partnership in the partner
portal — the same category, tier, stage, contact-tag, and visibility rules
that gate the room there. If a partnership has no accessible room of that
type, its leads or deals endpoints return 403 forbidden with
"The Leads area is not available in your partner portal. Ask the vendor to enable it."
or
"The Deals area is not available in your partner portal. Ask the vendor to enable it.".
Ask the vendor to enable the relevant area in the partner portal. Deal creation
can additionally be disabled independently of read/write access, via the deals
room’s settings — in that case POST /v1/partner/{partnershipId}/deals
returns 403 forbidden with
"Deal creation is disabled in your partner portal. Ask the vendor to enable deal creation.",
while reading and updating existing deals is unaffected.
Co-sell deals
When the vendor has the Co-selling module enabled, it can add your partnership to
another partner’s deal as a supporting partner. The deal then shows up in
GET /v1/partner/{partnershipId}/deals and
GET /v1/partner/{partnershipId}/deals/{dealId}, and you can update it with
PATCH, for as long as your partnership is an active partner of the deal’s
partner type and the module stays on. If the vendor removes you from the deal
or turns the module off, the deal is no longer visible to you.
Every deal response carries partners: every partner on the deal, with the
primary partner (is_primary: true, the deal’s own partner) first, then each
supporting partner, oldest first, with its role (a label the vendor defines,
or null). The primary partner can carry a role too, such as “Sourced” or
“Sourced + Co-sold”: the one the vendor picked for the deal, or the vendor’s
first primary role when none was picked. It is null when the vendor has no
primary roles for the deal’s partner type. Partners can see these roles but
not change them.
As a supporting partner you work the deal without its money and its lead:
- Money fields come back empty:
total_commission_value and
total_commission_occurrences are null, commission and spiff values are
not returned, and has_quote is false.
- The deal’s lead is not reachable. The leads endpoints do not return it, and
you cannot create deals from it.
- Archiving the deal, creating deals on its lead, and the deal’s payments stay
with the primary partner.
- The primary partner’s distributor link (
partnership.partnership_distributors_id,
distributor_name and the distributor logos) comes back null.
- The primary partner’s deal-stage lock binds you too. When it is on,
lock_deal_stage is true in the deal list, and changing fields.stage_id
returns 403 forbidden with the locked_by_partnership reason.
On a co-sell deal, assigned_contacts holds the contacts of every partner on
the deal, so you see the primary’s and other supporting partners’ contacts
beside your own.
Reference
Every partner endpoint — full request and response schemas — is documented in
the Partner API Reference section of these docs, generated from the same
spec this guide describes.