# Get current account info Source: https://docs.journeybee.io/api-reference/account/get-current-account-info /openapi.json get /me Returns the authenticated company, API key permissions, and enabled modules. Use this to check what you can access. # At-risk learners Source: https://docs.journeybee.io/api-reference/analytics/at-risk-learners /openapi.json get /analytics/lms/at-risk-learners Learners in progress for over 30 days without completing — candidates for a nudge. # Certification enrolments (paginated) Source: https://docs.journeybee.io/api-reference/analytics/certification-enrolments-paginated /openapi.json get /analytics/lms/enrollments Per-learner enrolment rows for a status (in_progress, completed, or failed) over the period. Paginated. # Commission leaderboard by partner Source: https://docs.journeybee.io/api-reference/analytics/commission-leaderboard-by-partner /openapi.json get /analytics/commission/partners Partners ranked by commission earned (and pending) for the period, in the company base currency, with payment + deal counts. Defaults to the current year. # Commission over time Source: https://docs.journeybee.io/api-reference/analytics/commission-over-time /openapi.json get /analytics/commission/time-series Commission paid and pending per bucket over the period, in the company base currency, with payment counts. Pass partner/category/tier/stage filters. Defaults to the current year. # Commission summary Source: https://docs.journeybee.io/api-reference/analytics/commission-summary /openapi.json get /analytics/commission-summary Company-wide commission totals for a period — paid (earned), pending, and processing — in the company base currency, plus payment + deal counts. Use this for 'how much commission is owed/earned this period'. This is commission MONEY; for commission RULES use getCommissionReport. Defaults to the current year; pass a `period` or `start_date`+`end_date`. Money is in whole currency units of `currency_code`. # Company revenue summary Source: https://docs.journeybee.io/api-reference/analytics/company-revenue-summary /openapi.json get /analytics/revenue-summary Company-wide totals for a period — revenue, deals won/total, average deal value, partner + lead counts, conversion and win rates — all in the company base currency. Defaults to the current year; pass a `period` or `start_date`+`end_date`. Money is in whole currency units of `currency_code`. # Deals won vs lost over time Source: https://docs.journeybee.io/api-reference/analytics/deals-won-vs-lost-over-time /openapi.json get /analytics/deals-won-lost Monthly won/lost deal counts and their base-currency value across all partners (or a `partner_uuids` subset). Deals are bucketed by close month; value is in `currency_code`. Defaults to the current year. # Engagement action breakdown Source: https://docs.journeybee.io/api-reference/analytics/engagement-action-breakdown /openapi.json get /analytics/engagement/action-breakdown Counts of each partner action type for the period. # Engagement over time Source: https://docs.journeybee.io/api-reference/analytics/engagement-over-time /openapi.json get /analytics/engagement-time-series Sessions, page views and unique users bucketed over the period. # Engagement summary Source: https://docs.journeybee.io/api-reference/analytics/engagement-summary /openapi.json get /analytics/engagement-summary Portal engagement totals for a period: unique users, sessions, page views, actions, and average session duration. Defaults to the current year; pass a `period` or `start_date`+`end_date`. # Hardest certification quizzes Source: https://docs.journeybee.io/api-reference/analytics/hardest-certification-quizzes /openapi.json get /analytics/lms/cert-resource-pass-rates Certification-bound quiz blocks ranked by lowest pass rate for the period. # Hardest certifications Source: https://docs.journeybee.io/api-reference/analytics/hardest-certifications /openapi.json get /analytics/lms/top-certifications-by-difficulty Certifications ranked by lowest pass rate (hardest) for the period. # Leads sent vs received over time Source: https://docs.journeybee.io/api-reference/analytics/leads-sent-vs-received-over-time /openapi.json get /analytics/leads-sent-received Monthly lead counts — received, sent, qualified (converted) and unqualified (rejected) — across all partners (or a `partner_uuids` subset). Defaults to the current year. # Learner leaderboard Source: https://docs.journeybee.io/api-reference/analytics/learner-leaderboard /openapi.json get /analytics/lms/users Top learners by certifications earned + average score for the period. # LMS over time Source: https://docs.journeybee.io/api-reference/analytics/lms-over-time /openapi.json get /analytics/lms-time-series Enrolments, completions and passes bucketed over the period. # LMS summary Source: https://docs.journeybee.io/api-reference/analytics/lms-summary /openapi.json get /analytics/lms-summary Training totals for a period: enrolments, started, completions, passes, pass rate, average score, active learners, certified users, average time to complete. Defaults to the current year. # Most-completed certification resources Source: https://docs.journeybee.io/api-reference/analytics/most-completed-certification-resources /openapi.json get /analytics/lms/cert-resource-completions Certification-bound resource blocks ranked by completions for the period. # Most-completed certifications Source: https://docs.journeybee.io/api-reference/analytics/most-completed-certifications /openapi.json get /analytics/lms/top-certifications-by-completion Certifications ranked by completions for the period. # One partner's deals by month Source: https://docs.journeybee.io/api-reference/analytics/one-partners-deals-by-month /openapi.json get /analytics/partners/deals-timeline A single partner's deals bucketed per month — won/lost/total counts and base-currency value. Requires `partner_uuid`. Value is in `currency_code`. Defaults to the last 6 months when no period is given. # One partner's leads by month Source: https://docs.journeybee.io/api-reference/analytics/one-partners-leads-by-month /openapi.json get /analytics/partners/leads-timeline A single partner's leads bucketed per month by status and direction (total, received, sent, converted, rejected, pending). Requires `partner_uuid`. Defaults to the last 6 months when no period is given. # Partner engagement leaderboard Source: https://docs.journeybee.io/api-reference/analytics/partner-engagement-leaderboard /openapi.json get /analytics/engagement/partners Partners ranked by portal engagement for the period. # Partner revenue leaderboard Source: https://docs.journeybee.io/api-reference/analytics/partner-revenue-leaderboard /openapi.json get /analytics/partners Partners ranked by revenue for a period, each deal converted to the company base currency. Use this to answer 'best/top partner by revenue/leads/deals' — do NOT list deals and sum them by hand (that mis-handles currencies). Defaults to the current year; pass a `period` or `start_date`+`end_date`. Money is in whole currency units of `currency_code`. # Partner training leaderboard Source: https://docs.journeybee.io/api-reference/analytics/partner-training-leaderboard /openapi.json get /analytics/lms/partners Partners ranked by training completion for the period. # Revenue & leads over time Source: https://docs.journeybee.io/api-reference/analytics/revenue-&-leads-over-time /openapi.json get /analytics/partners/revenue-time-series Revenue (deals WON in the bucket) and lead count per month over the period, in the company base currency. Pass `partner_uuids` for one partner's trend, or category/tier/stage filters. Defaults to the current year. # Top certifications by views Source: https://docs.journeybee.io/api-reference/analytics/top-certifications-by-views /openapi.json get /analytics/engagement/top-certifications Most-viewed certifications for the period. # Top resources by views Source: https://docs.journeybee.io/api-reference/analytics/top-resources-by-views /openapi.json get /analytics/engagement/top-resources Most-viewed resources for the period. # Top rooms by views Source: https://docs.journeybee.io/api-reference/analytics/top-rooms-by-views /openapi.json get /analytics/engagement/top-rooms Most-viewed rooms for the period. # Delete an asset Source: https://docs.journeybee.io/api-reference/assets/delete-an-asset /openapi.json delete /assets/{uuid} # Get an asset Source: https://docs.journeybee.io/api-reference/assets/get-an-asset /openapi.json get /assets/{uuid} # List assets Source: https://docs.journeybee.io/api-reference/assets/list-assets /openapi.json get /assets/ # Update an asset Source: https://docs.journeybee.io/api-reference/assets/update-an-asset /openapi.json patch /assets/{uuid} # Upload an asset from URL Source: https://docs.journeybee.io/api-reference/assets/upload-an-asset-from-url /openapi.json post /assets/ Fetches a file from the provided public URL and uploads it to Journeybee's CDN. Returns the asset with a UUID that can be used in resource blocks, room blocks, and other content. Maximum file size: 5GB. # Archive an attribution type Source: https://docs.journeybee.io/api-reference/attributions/archive-an-attribution-type /openapi.json delete /attributions/{uuid} Archives the attribution type. It will no longer appear in the default list but existing lead attributions referencing it are preserved. # Create an attribution type Source: https://docs.journeybee.io/api-reference/attributions/create-an-attribution-type /openapi.json post /attributions/ # Get an attribution type Source: https://docs.journeybee.io/api-reference/attributions/get-an-attribution-type /openapi.json get /attributions/{uuid} # List attribution types Source: https://docs.journeybee.io/api-reference/attributions/list-attribution-types /openapi.json get /attributions/ Returns attribution type definitions. Use the UUID when adding attributions to leads. # Update an attribution type Source: https://docs.journeybee.io/api-reference/attributions/update-an-attribution-type /openapi.json patch /attributions/{uuid} # List audit log entries Source: https://docs.journeybee.io/api-reference/audit-log/list-audit-log-entries /openapi.json get /audit-log/ List change-history entries for the company. Filter by entity_type (lead, lead_deal, partnership, lead_deal_payment) and/or entity_uuid for a single entity's trail, or omit both for a company-wide feed. Each entry records what changed (changes[]), the action, the rendered actor, and when. # Activate or deactivate an automation workflow Source: https://docs.journeybee.io/api-reference/automations/activate-or-deactivate-an-automation-workflow /openapi.json post /automations/{uuid}/toggle # Archive an automation workflow Source: https://docs.journeybee.io/api-reference/automations/archive-an-automation-workflow /openapi.json delete /automations/{uuid} # Create an automation workflow Source: https://docs.journeybee.io/api-reference/automations/create-an-automation-workflow /openapi.json post /automations/ # Get an automation workflow Source: https://docs.journeybee.io/api-reference/automations/get-an-automation-workflow /openapi.json get /automations/{uuid} # List automation workflows Source: https://docs.journeybee.io/api-reference/automations/list-automation-workflows /openapi.json get /automations/ # Update an automation workflow (label, description, graph) Source: https://docs.journeybee.io/api-reference/automations/update-an-automation-workflow-label-description-graph /openapi.json patch /automations/{uuid} # Add a category filter to a campaign Source: https://docs.journeybee.io/api-reference/campaign-audience/add-a-category-filter-to-a-campaign /openapi.json post /campaigns/{uuid}/categories # Add a contact tag filter to a campaign Source: https://docs.journeybee.io/api-reference/campaign-audience/add-a-contact-tag-filter-to-a-campaign /openapi.json post /campaigns/{uuid}/contact-tags # Add a partnership-stage filter to a campaign Source: https://docs.journeybee.io/api-reference/campaign-audience/add-a-partnership-stage-filter-to-a-campaign /openapi.json post /campaigns/{uuid}/stages # Add a specific partnership to a campaign audience Source: https://docs.journeybee.io/api-reference/campaign-audience/add-a-specific-partnership-to-a-campaign-audience /openapi.json post /campaigns/{uuid}/recipient-partnerships # Add a tier filter to a campaign Source: https://docs.journeybee.io/api-reference/campaign-audience/add-a-tier-filter-to-a-campaign /openapi.json post /campaigns/{uuid}/tiers # Add an explicit contact to a campaign Source: https://docs.journeybee.io/api-reference/campaign-audience/add-an-explicit-contact-to-a-campaign /openapi.json post /campaigns/{uuid}/contacts # Remove a category filter from a campaign Source: https://docs.journeybee.io/api-reference/campaign-audience/remove-a-category-filter-from-a-campaign /openapi.json delete /campaigns/{uuid}/categories/{subUuid} # Remove a contact tag filter from a campaign Source: https://docs.journeybee.io/api-reference/campaign-audience/remove-a-contact-tag-filter-from-a-campaign /openapi.json delete /campaigns/{uuid}/contact-tags/{subUuid} # Remove a partnership-stage filter from a campaign Source: https://docs.journeybee.io/api-reference/campaign-audience/remove-a-partnership-stage-filter-from-a-campaign /openapi.json delete /campaigns/{uuid}/stages/{subUuid} # Remove a specific partnership from a campaign audience Source: https://docs.journeybee.io/api-reference/campaign-audience/remove-a-specific-partnership-from-a-campaign-audience /openapi.json delete /campaigns/{uuid}/recipient-partnerships/{subUuid} # Remove a tier filter from a campaign Source: https://docs.journeybee.io/api-reference/campaign-audience/remove-a-tier-filter-from-a-campaign /openapi.json delete /campaigns/{uuid}/tiers/{subUuid} # Remove an explicit contact from a campaign Source: https://docs.journeybee.io/api-reference/campaign-audience/remove-an-explicit-contact-from-a-campaign /openapi.json delete /campaigns/{uuid}/contacts/{subUuid} # Remove a partner from a campaign Source: https://docs.journeybee.io/api-reference/campaign-partners/remove-a-partner-from-a-campaign /openapi.json delete /campaigns/{uuid}/partners/{subUuid} # Share a campaign with a partner Source: https://docs.journeybee.io/api-reference/campaign-partners/share-a-campaign-with-a-partner /openapi.json post /campaigns/{uuid}/partners Add or update a partner's access to the campaign. `shared` exposes the campaign in the partner portal; `shared_leads` shares its leads. # Archive a campaign Source: https://docs.journeybee.io/api-reference/campaigns/archive-a-campaign /openapi.json delete /campaigns/{uuid} # Campaign lead stats Source: https://docs.journeybee.io/api-reference/campaigns/campaign-lead-stats /openapi.json get /campaigns/lead-stats Lead counts attributed to each campaign (leads.campaign_id) plus a grand total, in a single call. Ordered by lead count descending; includes campaigns with zero leads. Use this to answer 'how many leads have campaigns produced' instead of fetching each campaign one by one. # Create a campaign Source: https://docs.journeybee.io/api-reference/campaigns/create-a-campaign /openapi.json post /campaigns/ Create a campaign — the send configuration (audience filters, partner sharing, and a LINK to one email template). A campaign is NOT the email content itself: to create the actual email (a "campaign email" — its header, body, footer), use createMarketingTemplate, then link it here via PATCH email_template_uuid. All fields are optional — a campaign is created in draft and configured afterwards via the sub-resource endpoints. Sending emails is not available through the API. # Get a campaign Source: https://docs.journeybee.io/api-reference/campaigns/get-a-campaign /openapi.json get /campaigns/{uuid} Get a campaign with its full audience configuration: tier/category/stage filters, shared partners, partner-type flags, and linked email template. # List a campaign's leads Source: https://docs.journeybee.io/api-reference/campaigns/list-a-campaigns-leads /openapi.json get /campaigns/{uuid}/leads List the leads attributed to a campaign (leads.campaign_id). Supports search, status filtering, and pagination. Read-only — sending is done by a human in the app. # List campaigns Source: https://docs.journeybee.io/api-reference/campaigns/list-campaigns /openapi.json get /campaigns/ List campaigns with optional filtering by status, type, or search. Each campaign includes its linked email template, audience partner-type flags, and tags. # Preview campaign recipients Source: https://docs.journeybee.io/api-reference/campaigns/preview-campaign-recipients /openapi.json get /campaigns/{uuid}/recipients # Send a test email for a campaign Source: https://docs.journeybee.io/api-reference/campaigns/send-a-test-email-for-a-campaign /openapi.json post /campaigns/{uuid}/test-send # Update a campaign Source: https://docs.journeybee.io/api-reference/campaigns/update-a-campaign /openapi.json patch /campaigns/{uuid} Update campaign fields, including status, schedule, partner-type audience flags, and the linked email template (email_template_uuid; null clears it). Audience filters and partner sharing are managed via the sub-resource endpoints. # Assign a user to a kanban card Source: https://docs.journeybee.io/api-reference/card-assignees/assign-a-user-to-a-kanban-card /openapi.json post /cards/{uuid}/assignees # Unassign a user from a kanban card Source: https://docs.journeybee.io/api-reference/card-assignees/unassign-a-user-from-a-kanban-card /openapi.json delete /cards/{uuid}/assignees/{subUuid} # Link a partner to a kanban card Source: https://docs.journeybee.io/api-reference/card-partners/link-a-partner-to-a-kanban-card /openapi.json post /cards/{uuid}/partners # Unlink a partner from a kanban card Source: https://docs.journeybee.io/api-reference/card-partners/unlink-a-partner-from-a-kanban-card /openapi.json delete /cards/{uuid}/partners/{subUuid} # Add a tag to a kanban card Source: https://docs.journeybee.io/api-reference/card-tags/add-a-tag-to-a-kanban-card /openapi.json post /cards/{uuid}/tags # Remove a tag from a kanban card Source: https://docs.journeybee.io/api-reference/card-tags/remove-a-tag-from-a-kanban-card /openapi.json delete /cards/{uuid}/tags/{subUuid} # Archive a kanban card Source: https://docs.journeybee.io/api-reference/cards/archive-a-kanban-card /openapi.json delete /cards/{uuid} Archives (soft-deletes) a kanban card. # Get a kanban card Source: https://docs.journeybee.io/api-reference/cards/get-a-kanban-card /openapi.json get /cards/{uuid} Returns a kanban card's metadata: title, current stage, the board's available stages (valid stage-move targets), assigned users, tags and linked partners. The rich-text body is edited separately. # Update a kanban card Source: https://docs.journeybee.io/api-reference/cards/update-a-kanban-card /openapi.json patch /cards/{uuid} Rename a kanban card and/or move it to another stage. Use a stage_uuid from the card's available_stages (see getCard). # Create a category Source: https://docs.journeybee.io/api-reference/categories/create-a-category /openapi.json post /categories/ # Delete a category Source: https://docs.journeybee.io/api-reference/categories/delete-a-category /openapi.json delete /categories/{uuid} # List categories Source: https://docs.journeybee.io/api-reference/categories/list-categories /openapi.json get /categories/ # Update a category Source: https://docs.journeybee.io/api-reference/categories/update-a-category /openapi.json patch /categories/{uuid} # Add a category to a certification Source: https://docs.journeybee.io/api-reference/certification-categories/add-a-category-to-a-certification /openapi.json post /certifications/{uuid}/categories # Remove a category from a certification Source: https://docs.journeybee.io/api-reference/certification-categories/remove-a-category-from-a-certification /openapi.json delete /certifications/{uuid}/categories/{subUuid} # Enroll a user in a certification Source: https://docs.journeybee.io/api-reference/certification-enrollments/enroll-a-user-in-a-certification /openapi.json post /certifications/{uuid}/enrollments # List enrollments for a certification Source: https://docs.journeybee.io/api-reference/certification-enrollments/list-enrollments-for-a-certification /openapi.json get /certifications/{uuid}/enrollments # Add a folder to a certification Source: https://docs.journeybee.io/api-reference/certification-folders/add-a-folder-to-a-certification /openapi.json post /certifications/{uuid}/folders # Remove a folder from a certification Source: https://docs.journeybee.io/api-reference/certification-folders/remove-a-folder-from-a-certification /openapi.json delete /certifications/{uuid}/folders/{subUuid} # Add a curriculum resource to a certification Source: https://docs.journeybee.io/api-reference/certification-resources/add-a-curriculum-resource-to-a-certification /openapi.json post /certifications/{uuid}/resources # List curriculum resources for a certification Source: https://docs.journeybee.io/api-reference/certification-resources/list-curriculum-resources-for-a-certification /openapi.json get /certifications/{uuid}/resources # Remove a curriculum resource from a certification Source: https://docs.journeybee.io/api-reference/certification-resources/remove-a-curriculum-resource-from-a-certification /openapi.json delete /certifications/{uuid}/resources/{subUuid} # Update a curriculum resource in a certification Source: https://docs.journeybee.io/api-reference/certification-resources/update-a-curriculum-resource-in-a-certification /openapi.json patch /certifications/{uuid}/resources/{subUuid} # Archive a certification Source: https://docs.journeybee.io/api-reference/certifications/archive-a-certification /openapi.json delete /certifications/{uuid} # Create a certification Source: https://docs.journeybee.io/api-reference/certifications/create-a-certification /openapi.json post /certifications/ # Get a certification Source: https://docs.journeybee.io/api-reference/certifications/get-a-certification /openapi.json get /certifications/{uuid} # List certifications Source: https://docs.journeybee.io/api-reference/certifications/list-certifications /openapi.json get /certifications/ # Update a certification Source: https://docs.journeybee.io/api-reference/certifications/update-a-certification /openapi.json patch /certifications/{uuid} # Create a commission rule Source: https://docs.journeybee.io/api-reference/commissions/create-a-commission-rule /openapi.json post /commissions/ Create a commission rule with calculation entries. Optionally scope to specific tiers, categories, or partnerships via their UUIDs. ## Calculation structure The `calculation` object contains `entries` — an array of payment triggers: ```json { "entries": [ { "payment_stage_id": 123, "calculation": { "type": "oneOff", "option": "percentage", "value": 10 } } ] } ``` ### Calculation types - **oneOff**: Single payment when stage is reached. `option`: fixed (flat amount), percentage (of deal value), or scaling (varies by custom field). - **recurring**: Multiple payments over time. Additional fields: `recurring_period` (weekly/monthly/quarterly/yearly), `number_of_occurrences`. Supports `periodRates` for variable rates per period. - **split**: Different calculations for one-off and recurring components. Contains `split.oneOff` and `split.recurring` sub-calculations. ### Scaling When `option` is `scaling`, provide `scalingConfig` with `customFieldId` and `tiers` array mapping custom field option IDs to fixed/percentage values. ### Scoping Commissions can be scoped to apply only to specific partners: - `tier_uuids` — apply to partners in these tiers - `category_uuids` — apply to partners in these categories - `partnership_uuids` — apply to these specific partnerships (highest priority) # Delete a commission rule Source: https://docs.journeybee.io/api-reference/commissions/delete-a-commission-rule /openapi.json delete /commissions/{uuid} Permanently deletes a commission rule and all its tier, category, and partnership assignments. Existing deal commissions that were applied from this rule are not affected. # Get a commission rule Source: https://docs.journeybee.io/api-reference/commissions/get-a-commission-rule /openapi.json get /commissions/{uuid} # List commission rules Source: https://docs.journeybee.io/api-reference/commissions/list-commission-rules /openapi.json get /commissions/ Returns commission rule configurations. Filter by `partner_type` (referral/reseller/distributor) or `payment_target` (`partnership` for partner commissions, `individual` for spiffs paid to individual users/contacts). Each commission rule contains a `calculation` object with `entries` — an array of payment triggers. Each entry specifies when payment occurs (`payment_stage_id`) and how it's calculated (`calculation.type`: oneOff, recurring, or split). # Update a commission rule Source: https://docs.journeybee.io/api-reference/commissions/update-a-commission-rule /openapi.json patch /commissions/{uuid} Update commission rule properties or calculation entries. When `tier_uuids`, `category_uuids`, or `partnership_uuids` are provided, they **replace** the current assignments entirely (not merge). Omit these fields to leave assignments unchanged. See POST /v1/commissions for the full calculation schema documentation. # Get a contact Source: https://docs.journeybee.io/api-reference/contacts/get-a-contact /openapi.json get /contacts/{uuid} # List partner contacts Source: https://docs.journeybee.io/api-reference/contacts/list-partner-contacts /openapi.json get /contacts/ Returns all partner contacts across partnerships. Filter by partnership_uuid to scope to a specific partner. Each contact includes its partnership reference. # List available currencies Source: https://docs.journeybee.io/api-reference/currencies/list-available-currencies /openapi.json get /currencies/ Returns all available currencies. Use the UUID when setting currency on products, deals, or pricing calculators. # Add an option to a select/multi-select custom field Source: https://docs.journeybee.io/api-reference/custom-field-options/add-an-option-to-a-selectmulti-select-custom-field /openapi.json post /custom-fields/{uuid}/options # Delete an option from a custom field Source: https://docs.journeybee.io/api-reference/custom-field-options/delete-an-option-from-a-custom-field /openapi.json delete /custom-fields/{uuid}/options/{optionId} Returns 400 if deleting this option would leave a select/multi_select custom field with no options. # Update an option on a custom field Source: https://docs.journeybee.io/api-reference/custom-field-options/update-an-option-on-a-custom-field /openapi.json patch /custom-fields/{uuid}/options/{optionId} # Create a custom field rule Source: https://docs.journeybee.io/api-reference/custom-field-rules/create-a-custom-field-rule /openapi.json post /custom-field-rules/ # Delete a custom field rule Source: https://docs.journeybee.io/api-reference/custom-field-rules/delete-a-custom-field-rule /openapi.json delete /custom-field-rules/{uuid} # List custom field rules Source: https://docs.journeybee.io/api-reference/custom-field-rules/list-custom-field-rules /openapi.json get /custom-field-rules/ # Update a custom field rule Source: https://docs.journeybee.io/api-reference/custom-field-rules/update-a-custom-field-rule /openapi.json patch /custom-field-rules/{uuid} # Delete a custom field value Source: https://docs.journeybee.io/api-reference/custom-field-values/delete-a-custom-field-value /openapi.json delete /custom-field-values/{uuid} Deletes are validated server-side too — clearing a visible required value, or a value locked by a custom field rule, is rejected (`reason: required_cannot_blank` or `locked_by_rule` in the error response's `details`). # List custom field values for an entity Source: https://docs.journeybee.io/api-reference/custom-field-values/list-custom-field-values-for-an-entity /openapi.json get /custom-field-values/ # Set a custom field value on an entity Source: https://docs.journeybee.io/api-reference/custom-field-values/set-a-custom-field-value-on-an-entity /openapi.json post /custom-field-values/ Custom field values are validated server-side — required fields, rules, visibility, and locks are all enforced; a validation failure returns a 400 with per-field `details`, each entry carrying a machine-readable `reason`. # Update a custom field value Source: https://docs.journeybee.io/api-reference/custom-field-values/update-a-custom-field-value /openapi.json patch /custom-field-values/{uuid} Custom field values are validated server-side — required fields, rules, visibility, and locks are all enforced; a validation failure returns a 400 with per-field `details`, each entry carrying a machine-readable `reason`. # Create a custom field definition Source: https://docs.journeybee.io/api-reference/custom-fields/create-a-custom-field-definition /openapi.json post /custom-fields/ # Delete a custom field definition Source: https://docs.journeybee.io/api-reference/custom-fields/delete-a-custom-field-definition /openapi.json delete /custom-fields/{uuid} # List custom field definitions Source: https://docs.journeybee.io/api-reference/custom-fields/list-custom-field-definitions /openapi.json get /custom-fields/ # Update a custom field definition Source: https://docs.journeybee.io/api-reference/custom-fields/update-a-custom-field-definition /openapi.json patch /custom-fields/{uuid} # Clear the commission calculation from a deal Source: https://docs.journeybee.io/api-reference/deal-commission/clear-the-commission-calculation-from-a-deal /openapi.json delete /deals/{uuid}/commission Resets the deal's commission calculation to empty. Existing payments are not affected. # Get the commission calculation for a deal Source: https://docs.journeybee.io/api-reference/deal-commission/get-the-commission-calculation-for-a-deal /openapi.json get /deals/{uuid}/commission Returns the commission calculation applied to this deal. The `calculation` contains `entries` — each with a `payment_stage_id` (when payment triggers) and a `calculation` object defining the type (oneOff/recurring/split), option (fixed/percentage/scaling), and value. An entry may also carry a manual `override` (a fixed `amount` + `currency`, optional `note` and attached `document`) that replaces the calculated value. Returns `null` calculation if no commission is set. # Set the commission calculation for a deal Source: https://docs.journeybee.io/api-reference/deal-commission/set-the-commission-calculation-for-a-deal /openapi.json put /deals/{uuid}/commission Override the commission calculation on a deal. This replaces the entire `calculation` structure. Use this to manually adjust commissions after they've been auto-applied from a commission rule. The structure matches the commission rule format — see POST /v1/commissions for the full calculation schema documentation. Each entry may carry a manual `override` to set a fixed commission instead of the calculation: provide `amount` (whole currency units), `currency_code`, and optionally a markdown `note` and a `document_url` (pdf/csv/excel, fetched into Journeybee's CDN) or `document_asset_uuid`. Manual overrides require the company's commission-overrides setting to be enabled (otherwise `403`). Setting the commission recalculates the deal's pending payment records. # Remove a custom field value from a deal Source: https://docs.journeybee.io/api-reference/deal-custom-field-values/remove-a-custom-field-value-from-a-deal /openapi.json delete /deals/{uuid}/custom-fields/{subUuid} Deletes are validated server-side too — clearing a visible required value, or a value locked by a custom field rule, is rejected (`reason: required_cannot_blank` or `locked_by_rule` in the error response's `details`). # Set a custom field value on a deal Source: https://docs.journeybee.io/api-reference/deal-custom-field-values/set-a-custom-field-value-on-a-deal /openapi.json put /deals/{uuid}/custom-fields Set or update a custom field value. Custom fields are scoped by partner type — only fields matching the deal's lead partnership type will be relevant. Values are validated server-side — required fields, rules, visibility, and locks are all enforced; a validation failure returns a 400 with per-field `details`, each entry carrying a machine-readable `reason`. # Create a note on a deal Source: https://docs.journeybee.io/api-reference/deal-notes/create-a-note-on-a-deal /openapi.json post /deals/{uuid}/notes # Delete a note from a deal Source: https://docs.journeybee.io/api-reference/deal-notes/delete-a-note-from-a-deal /openapi.json delete /deals/{uuid}/notes/{subUuid} # List notes for a deal Source: https://docs.journeybee.io/api-reference/deal-notes/list-notes-for-a-deal /openapi.json get /deals/{uuid}/notes # List payments for a deal Source: https://docs.journeybee.io/api-reference/deal-payments/list-payments-for-a-deal /openapi.json get /deals/{uuid}/payments Returns all payment records for a deal including commission and spiff payments, their approval status, and processing state. Payments are read-only via the API. # Add a product to a deal Source: https://docs.journeybee.io/api-reference/deal-products/add-a-product-to-a-deal /openapi.json post /deals/{uuid}/products # List products on a deal Source: https://docs.journeybee.io/api-reference/deal-products/list-products-on-a-deal /openapi.json get /deals/{uuid}/products # Remove a product from a deal Source: https://docs.journeybee.io/api-reference/deal-products/remove-a-product-from-a-deal /openapi.json delete /deals/{uuid}/products/{subUuid} Only manually added products can be removed. # Update a product on a deal Source: https://docs.journeybee.io/api-reference/deal-products/update-a-product-on-a-deal /openapi.json patch /deals/{uuid}/products/{subUuid} Update quantity or price override. Only manually added products can be updated (not quote-sourced or lead-sourced). # Create a deal stage Source: https://docs.journeybee.io/api-reference/deal-stages/create-a-deal-stage /openapi.json post /deal-stages/ # Delete a deal stage Source: https://docs.journeybee.io/api-reference/deal-stages/delete-a-deal-stage /openapi.json delete /deal-stages/{uuid} # List deal stages Source: https://docs.journeybee.io/api-reference/deal-stages/list-deal-stages /openapi.json get /deal-stages/ # Update a deal stage Source: https://docs.journeybee.io/api-reference/deal-stages/update-a-deal-stage /openapi.json patch /deal-stages/{uuid} # Add a tag to a deal Source: https://docs.journeybee.io/api-reference/deal-tags/add-a-tag-to-a-deal /openapi.json post /deals/{uuid}/tags # Remove a tag from a deal Source: https://docs.journeybee.io/api-reference/deal-tags/remove-a-tag-from-a-deal /openapi.json delete /deals/{uuid}/tags/{subUuid} # Create a task on a deal Source: https://docs.journeybee.io/api-reference/deal-tasks/create-a-task-on-a-deal /openapi.json post /deals/{uuid}/tasks Creates a task attached to this deal. Equivalent to POST /v1/tasks with deal_uuid set. # List tasks for a deal Source: https://docs.journeybee.io/api-reference/deal-tasks/list-tasks-for-a-deal /openapi.json get /deals/{uuid}/tasks # Archive a deal Source: https://docs.journeybee.io/api-reference/deals/archive-a-deal /openapi.json delete /deals/{uuid} # Create a deal Source: https://docs.journeybee.io/api-reference/deals/create-a-deal /openapi.json post /deals/ Create a deal on a lead. A lead is required. Multiple deals can exist on the same lead. If no stage is provided, the first deal stage for the lead's partner type is used. If no currency is provided, the company's default currency is used. Deal stages and custom fields are scoped by partner type. Custom fields may be required or rule-controlled (visibility, locking, auto-population) — validation errors return per-field `details` with a machine-readable `reason`. See the Custom Field Validation section of the Error Handling guide. # Get a deal Source: https://docs.journeybee.io/api-reference/deals/get-a-deal /openapi.json get /deals/{uuid} # Get pipeline summary Source: https://docs.journeybee.io/api-reference/deals/get-pipeline-summary /openapi.json get /deals/pipeline-summary Get a summary of the deal pipeline with deal counts and total values per stage. Values are converted to the company's base currency using locked rates (for won/lost deals) or live exchange rates. # List deals Source: https://docs.journeybee.io/api-reference/deals/list-deals /openapi.json get /deals/ List deals with optional filtering by stage phase, stage UUID, lead, partnership, or search. Each deal includes its linked lead, partner, stage, currency, tags, and custom field values. Note: deal stages and custom fields are scoped by partner type (referral/reseller/distributor). # Update a deal Source: https://docs.journeybee.io/api-reference/deals/update-a-deal /openapi.json patch /deals/{uuid} Update deal fields. Changing `deal_value` automatically recalculates `total_commission_value` and rebuilds pending payment records based on the deal's current commission calculation. # Create a folder Source: https://docs.journeybee.io/api-reference/folders/create-a-folder /openapi.json post /folders/ # Delete a folder Source: https://docs.journeybee.io/api-reference/folders/delete-a-folder /openapi.json delete /folders/{uuid} # Get a folder Source: https://docs.journeybee.io/api-reference/folders/get-a-folder /openapi.json get /folders/{uuid} # List folders Source: https://docs.journeybee.io/api-reference/folders/list-folders /openapi.json get /folders/ # Update a folder Source: https://docs.journeybee.io/api-reference/folders/update-a-folder /openapi.json patch /folders/{uuid} # Add an attribution to a lead Source: https://docs.journeybee.io/api-reference/lead-attributions/add-an-attribution-to-a-lead /openapi.json post /leads/{uuid}/attributions # List attributions for a lead Source: https://docs.journeybee.io/api-reference/lead-attributions/list-attributions-for-a-lead /openapi.json get /leads/{uuid}/attributions # Remove an attribution from a lead Source: https://docs.journeybee.io/api-reference/lead-attributions/remove-an-attribution-from-a-lead /openapi.json delete /leads/{uuid}/attributions/{subUuid} # Add a contact to a lead Source: https://docs.journeybee.io/api-reference/lead-contacts/add-a-contact-to-a-lead /openapi.json post /leads/{uuid}/contacts Assign a partner contact to a lead. The contact must belong to the same partnership as the lead. # List contacts on a lead Source: https://docs.journeybee.io/api-reference/lead-contacts/list-contacts-on-a-lead /openapi.json get /leads/{uuid}/contacts Returns partner contacts assigned to a lead. Contacts belong to the lead's partnership. # Remove a contact from a lead Source: https://docs.journeybee.io/api-reference/lead-contacts/remove-a-contact-from-a-lead /openapi.json delete /leads/{uuid}/contacts/{subUuid} # Remove a custom field value from a lead Source: https://docs.journeybee.io/api-reference/lead-custom-field-values/remove-a-custom-field-value-from-a-lead /openapi.json delete /leads/{uuid}/custom-fields/{subUuid} Deletes are validated server-side too — clearing a visible required value, or a value locked by a custom field rule, is rejected (`reason: required_cannot_blank` or `locked_by_rule` in the error response's `details`). # Set a custom field value on a lead Source: https://docs.journeybee.io/api-reference/lead-custom-field-values/set-a-custom-field-value-on-a-lead /openapi.json put /leads/{uuid}/custom-fields Custom field values are validated server-side — required fields, rules, visibility, and locks are all enforced; a validation failure returns a 400 with per-field `details`, each entry carrying a machine-readable `reason`. # Update a custom field value on a lead Source: https://docs.journeybee.io/api-reference/lead-custom-field-values/update-a-custom-field-value-on-a-lead /openapi.json patch /leads/{uuid}/custom-fields/{subUuid} Custom field values are validated server-side — required fields, rules, visibility, and locks are all enforced; a validation failure returns a 400 with per-field `details`, each entry carrying a machine-readable `reason`. # Assign a distributor to a lead Source: https://docs.journeybee.io/api-reference/lead-distributor/assign-a-distributor-to-a-lead /openapi.json put /leads/{uuid}/distributor # Get the assigned distributor for a lead Source: https://docs.journeybee.io/api-reference/lead-distributor/get-the-assigned-distributor-for-a-lead /openapi.json get /leads/{uuid}/distributor # Remove the assigned distributor from a lead Source: https://docs.journeybee.io/api-reference/lead-distributor/remove-the-assigned-distributor-from-a-lead /openapi.json delete /leads/{uuid}/distributor # Create a note on a lead Source: https://docs.journeybee.io/api-reference/lead-notes/create-a-note-on-a-lead /openapi.json post /leads/{uuid}/notes # Delete a note from a lead Source: https://docs.journeybee.io/api-reference/lead-notes/delete-a-note-from-a-lead /openapi.json delete /leads/{uuid}/notes/{subUuid} # List notes for a lead Source: https://docs.journeybee.io/api-reference/lead-notes/list-notes-for-a-lead /openapi.json get /leads/{uuid}/notes # Add a product to a lead Source: https://docs.journeybee.io/api-reference/lead-products/add-a-product-to-a-lead /openapi.json post /leads/{uuid}/products Add a product from the catalog to a lead. Returns 409 if the lead has a quote attached (products become read-only when managed by a quote). # List products on a lead Source: https://docs.journeybee.io/api-reference/lead-products/list-products-on-a-lead /openapi.json get /leads/{uuid}/products Returns all products attached to a lead, including both manually added products and products sourced from a quote. The `source` field indicates the origin. # Remove a product from a lead Source: https://docs.journeybee.io/api-reference/lead-products/remove-a-product-from-a-lead /openapi.json delete /leads/{uuid}/products/{subUuid} Remove a manually added product from a lead. Returns 409 if the lead has a quote attached. Only manual products can be removed (not quote-sourced). # Update a product on a lead Source: https://docs.journeybee.io/api-reference/lead-products/update-a-product-on-a-lead /openapi.json patch /leads/{uuid}/products/{subUuid} Update quantity or price override for a manually added product. Returns 409 if the lead has a quote attached. Only manual products can be updated (not quote-sourced). # Delete the quote from a lead Source: https://docs.journeybee.io/api-reference/lead-quotes/delete-the-quote-from-a-lead /openapi.json delete /leads/{uuid}/quote Removes the quote and all its line items from a lead. Also removes any quote-sourced products from the lead, restoring manual product management. # Get the quote for a lead Source: https://docs.journeybee.io/api-reference/lead-quotes/get-the-quote-for-a-lead /openapi.json get /leads/{uuid}/quote Returns the quote attached to a lead with all line items, currency, and pricing calculator reference. Returns null if no quote exists. # Add a tag to a lead Source: https://docs.journeybee.io/api-reference/lead-tags/add-a-tag-to-a-lead /openapi.json post /leads/{uuid}/tags # Remove a tag from a lead Source: https://docs.journeybee.io/api-reference/lead-tags/remove-a-tag-from-a-lead /openapi.json delete /leads/{uuid}/tags/{subUuid} # Create a task on a lead Source: https://docs.journeybee.io/api-reference/lead-tasks/create-a-task-on-a-lead /openapi.json post /leads/{uuid}/tasks Creates a task attached to this lead. Equivalent to POST /v1/tasks with lead_uuid set. # List tasks for a lead Source: https://docs.journeybee.io/api-reference/lead-tasks/list-tasks-for-a-lead /openapi.json get /leads/{uuid}/tasks # Assign a user to a lead Source: https://docs.journeybee.io/api-reference/lead-users/assign-a-user-to-a-lead /openapi.json post /leads/{uuid}/assigned-users # Remove an assigned user from a lead Source: https://docs.journeybee.io/api-reference/lead-users/remove-an-assigned-user-from-a-lead /openapi.json delete /leads/{uuid}/assigned-users/{subUuid} # Archive a lead Source: https://docs.journeybee.io/api-reference/leads/archive-a-lead /openapi.json delete /leads/{uuid} # Create a lead Source: https://docs.journeybee.io/api-reference/leads/create-a-lead /openapi.json post /leads/ Custom fields may be required or rule-controlled (visibility, locking, auto-population) — validation errors return per-field `details` with a machine-readable `reason`. See the Custom Field Validation section of the Error Handling guide. # Get a lead Source: https://docs.journeybee.io/api-reference/leads/get-a-lead /openapi.json get /leads/{uuid} # List leads Source: https://docs.journeybee.io/api-reference/leads/list-leads /openapi.json get /leads/ # Update a lead Source: https://docs.journeybee.io/api-reference/leads/update-a-lead /openapi.json patch /leads/{uuid} # Archive a marketing email template Source: https://docs.journeybee.io/api-reference/marketing-templates/archive-a-marketing-email-template /openapi.json delete /marketing-templates/{uuid} # Create a marketing email template Source: https://docs.journeybee.io/api-reference/marketing-templates/create-a-marketing-email-template /openapi.json post /marketing-templates/ Create the actual marketing EMAIL — a "campaign email" / email template: the content recipients see (header, body, footer blocks). Distinct from a campaign, which only references a template and defines its audience. Seeded with empty content; name, subject, and channel are optional. To build its blocks, navigate the user to /go/template/ (the returned uuid) — the block-builder tools become available once that editor opens. # Get a marketing email template Source: https://docs.journeybee.io/api-reference/marketing-templates/get-a-marketing-email-template /openapi.json get /marketing-templates/{uuid} # List marketing email templates Source: https://docs.journeybee.io/api-reference/marketing-templates/list-marketing-email-templates /openapi.json get /marketing-templates/ List email templates with optional search. Each template reports how many campaigns reference it. Block content is edited through the in-app builder, not the API. # Update a marketing email template Source: https://docs.journeybee.io/api-reference/marketing-templates/update-a-marketing-email-template /openapi.json patch /marketing-templates/{uuid} Update template metadata: name, subject, channel, partner visibility, and live status. Block content is edited through the in-app builder, not the API. # Create a contact for a partner Source: https://docs.journeybee.io/api-reference/partner-contacts/create-a-contact-for-a-partner /openapi.json post /partners/{uuid}/contacts # Delete a contact Source: https://docs.journeybee.io/api-reference/partner-contacts/delete-a-contact /openapi.json delete /partners/{uuid}/contacts/{subUuid} # List contacts for a partner Source: https://docs.journeybee.io/api-reference/partner-contacts/list-contacts-for-a-partner /openapi.json get /partners/{uuid}/contacts # Update a contact Source: https://docs.journeybee.io/api-reference/partner-contacts/update-a-contact /openapi.json patch /partners/{uuid}/contacts/{subUuid} # Remove a custom field value from a partner Source: https://docs.journeybee.io/api-reference/partner-custom-field-values/remove-a-custom-field-value-from-a-partner /openapi.json delete /partners/{uuid}/custom-fields/{subUuid} Deletes are validated server-side too — clearing a visible required value, or a value locked by a custom field rule, is rejected (`reason: required_cannot_blank` or `locked_by_rule` in the error response's `details`). # Set a custom field value on a partner Source: https://docs.journeybee.io/api-reference/partner-custom-field-values/set-a-custom-field-value-on-a-partner /openapi.json post /partners/{uuid}/custom-fields Custom field values are validated server-side — required fields, rules, visibility, and locks are all enforced; a validation failure returns a 400 with per-field `details`, each entry carrying a machine-readable `reason`. # Update a custom field value on a partner Source: https://docs.journeybee.io/api-reference/partner-custom-field-values/update-a-custom-field-value-on-a-partner /openapi.json patch /partners/{uuid}/custom-fields/{subUuid} Custom field values are validated server-side — required fields, rules, visibility, and locks are all enforced; a validation failure returns a 400 with per-field `details`, each entry carrying a machine-readable `reason`. # Add a domain to a partner Source: https://docs.journeybee.io/api-reference/partner-domains/add-a-domain-to-a-partner /openapi.json post /partners/{uuid}/domains # Remove a domain from a partner Source: https://docs.journeybee.io/api-reference/partner-domains/remove-a-domain-from-a-partner /openapi.json delete /partners/{uuid}/domains/{subUuid} # Create a note on a partner Source: https://docs.journeybee.io/api-reference/partner-notes/create-a-note-on-a-partner /openapi.json post /partners/{uuid}/notes # Delete a note from a partner Source: https://docs.journeybee.io/api-reference/partner-notes/delete-a-note-from-a-partner /openapi.json delete /partners/{uuid}/notes/{subUuid} # List notes for a partner Source: https://docs.journeybee.io/api-reference/partner-notes/list-notes-for-a-partner /openapi.json get /partners/{uuid}/notes # Assign a reseller to a distributor Source: https://docs.journeybee.io/api-reference/partner-resellers/assign-a-reseller-to-a-distributor /openapi.json post /partners/{uuid}/resellers # List resellers assigned to a distributor Source: https://docs.journeybee.io/api-reference/partner-resellers/list-resellers-assigned-to-a-distributor /openapi.json get /partners/{uuid}/resellers # Remove a reseller from a distributor Source: https://docs.journeybee.io/api-reference/partner-resellers/remove-a-reseller-from-a-distributor /openapi.json delete /partners/{uuid}/resellers/{subUuid} # Create a partner stage Source: https://docs.journeybee.io/api-reference/partner-stages/create-a-partner-stage /openapi.json post /partner-stages/ # Delete a partner stage Source: https://docs.journeybee.io/api-reference/partner-stages/delete-a-partner-stage /openapi.json delete /partner-stages/{uuid} # List partner stages Source: https://docs.journeybee.io/api-reference/partner-stages/list-partner-stages /openapi.json get /partner-stages/ # Update a partner stage Source: https://docs.journeybee.io/api-reference/partner-stages/update-a-partner-stage /openapi.json patch /partner-stages/{uuid} # Add a tag to a partner Source: https://docs.journeybee.io/api-reference/partner-tags/add-a-tag-to-a-partner /openapi.json post /partners/{uuid}/tags # Remove a tag from a partner Source: https://docs.journeybee.io/api-reference/partner-tags/remove-a-tag-from-a-partner /openapi.json delete /partners/{uuid}/tags/{subUuid} # Assign a user to a partner Source: https://docs.journeybee.io/api-reference/partner-users/assign-a-user-to-a-partner /openapi.json post /partners/{uuid}/assigned-users # Remove a user from a partner Source: https://docs.journeybee.io/api-reference/partner-users/remove-a-user-from-a-partner /openapi.json delete /partners/{uuid}/assigned-users/{subUuid} # Archive a partner Source: https://docs.journeybee.io/api-reference/partners/archive-a-partner /openapi.json delete /partners/{uuid} Soft-deletes a partner by setting archived_at. The partner will no longer appear in list results unless include_archived is set. # Create a partner Source: https://docs.journeybee.io/api-reference/partners/create-a-partner /openapi.json post /partners/ Custom fields may be required or rule-controlled (visibility, locking, auto-population) — validation errors return per-field `details` with a machine-readable `reason`. See the Custom Field Validation section of the Error Handling guide. # Get a partner Source: https://docs.journeybee.io/api-reference/partners/get-a-partner /openapi.json get /partners/{uuid} # List partners Source: https://docs.journeybee.io/api-reference/partners/list-partners /openapi.json get /partners/ # Update a partner Source: https://docs.journeybee.io/api-reference/partners/update-a-partner /openapi.json patch /partners/{uuid} # Add a field to a pricing calculator Source: https://docs.journeybee.io/api-reference/pricing-calculator-fields/add-a-field-to-a-pricing-calculator /openapi.json post /pricing-calculators/{uuid}/fields # Remove a field from a pricing calculator Source: https://docs.journeybee.io/api-reference/pricing-calculator-fields/remove-a-field-from-a-pricing-calculator /openapi.json delete /pricing-calculators/{uuid}/fields/{subUuid} # Update a field on a pricing calculator Source: https://docs.journeybee.io/api-reference/pricing-calculator-fields/update-a-field-on-a-pricing-calculator /openapi.json patch /pricing-calculators/{uuid}/fields/{subUuid} # Add a formula to a pricing calculator Source: https://docs.journeybee.io/api-reference/pricing-calculator-formulas/add-a-formula-to-a-pricing-calculator /openapi.json post /pricing-calculators/{uuid}/formulas # Remove a formula from a pricing calculator Source: https://docs.journeybee.io/api-reference/pricing-calculator-formulas/remove-a-formula-from-a-pricing-calculator /openapi.json delete /pricing-calculators/{uuid}/formulas/{subUuid} # Update a formula on a pricing calculator Source: https://docs.journeybee.io/api-reference/pricing-calculator-formulas/update-a-formula-on-a-pricing-calculator /openapi.json patch /pricing-calculators/{uuid}/formulas/{subUuid} # Add a product to a pricing calculator Source: https://docs.journeybee.io/api-reference/pricing-calculator-products/add-a-product-to-a-pricing-calculator /openapi.json post /pricing-calculators/{uuid}/products # Remove a product from a pricing calculator Source: https://docs.journeybee.io/api-reference/pricing-calculator-products/remove-a-product-from-a-pricing-calculator /openapi.json delete /pricing-calculators/{uuid}/products/{subUuid} # Update a product on a pricing calculator Source: https://docs.journeybee.io/api-reference/pricing-calculator-products/update-a-product-on-a-pricing-calculator /openapi.json patch /pricing-calculators/{uuid}/products/{subUuid} # Archive a pricing calculator Source: https://docs.journeybee.io/api-reference/pricing-calculators/archive-a-pricing-calculator /openapi.json delete /pricing-calculators/{uuid} # Create a pricing calculator Source: https://docs.journeybee.io/api-reference/pricing-calculators/create-a-pricing-calculator /openapi.json post /pricing-calculators/ # Get a pricing calculator with fields, products, and formulas Source: https://docs.journeybee.io/api-reference/pricing-calculators/get-a-pricing-calculator-with-fields-products-and-formulas /openapi.json get /pricing-calculators/{uuid} # List pricing calculators Source: https://docs.journeybee.io/api-reference/pricing-calculators/list-pricing-calculators /openapi.json get /pricing-calculators/ # Update a pricing calculator Source: https://docs.journeybee.io/api-reference/pricing-calculators/update-a-pricing-calculator /openapi.json patch /pricing-calculators/{uuid} # Archive a product Source: https://docs.journeybee.io/api-reference/products/archive-a-product /openapi.json delete /products/{uuid} # Create a product Source: https://docs.journeybee.io/api-reference/products/create-a-product /openapi.json post /products/ # Get a product Source: https://docs.journeybee.io/api-reference/products/get-a-product /openapi.json get /products/{uuid} # List products Source: https://docs.journeybee.io/api-reference/products/list-products /openapi.json get /products/ # Update a product Source: https://docs.journeybee.io/api-reference/products/update-a-product /openapi.json patch /products/{uuid} # Add a block to a resource Source: https://docs.journeybee.io/api-reference/resource-blocks/add-a-block-to-a-resource /openapi.json post /resources/{uuid}/blocks # List blocks for a resource Source: https://docs.journeybee.io/api-reference/resource-blocks/list-blocks-for-a-resource /openapi.json get /resources/{uuid}/blocks # Remove a block from a resource Source: https://docs.journeybee.io/api-reference/resource-blocks/remove-a-block-from-a-resource /openapi.json delete /resources/{uuid}/blocks/{subUuid} # Update a resource block Source: https://docs.journeybee.io/api-reference/resource-blocks/update-a-resource-block /openapi.json patch /resources/{uuid}/blocks/{subUuid} # Add a category to a resource Source: https://docs.journeybee.io/api-reference/resource-categories/add-a-category-to-a-resource /openapi.json post /resources/{uuid}/categories # Remove a category from a resource Source: https://docs.journeybee.io/api-reference/resource-categories/remove-a-category-from-a-resource /openapi.json delete /resources/{uuid}/categories/{subUuid} # Add a resource to a folder Source: https://docs.journeybee.io/api-reference/resource-folders/add-a-resource-to-a-folder /openapi.json post /resources/{uuid}/folders # Remove a resource from a folder Source: https://docs.journeybee.io/api-reference/resource-folders/remove-a-resource-from-a-folder /openapi.json delete /resources/{uuid}/folders/{subUuid} # Add a tag to a resource Source: https://docs.journeybee.io/api-reference/resource-tags/add-a-tag-to-a-resource /openapi.json post /resources/{uuid}/tags # Remove a tag from a resource Source: https://docs.journeybee.io/api-reference/resource-tags/remove-a-tag-from-a-resource /openapi.json delete /resources/{uuid}/tags/{subUuid} # Archive a resource Source: https://docs.journeybee.io/api-reference/resources/archive-a-resource /openapi.json delete /resources/{uuid} # Create a resource Source: https://docs.journeybee.io/api-reference/resources/create-a-resource /openapi.json post /resources/ # Get a resource Source: https://docs.journeybee.io/api-reference/resources/get-a-resource /openapi.json get /resources/{uuid} # List resources Source: https://docs.journeybee.io/api-reference/resources/list-resources /openapi.json get /resources/ # Update a resource Source: https://docs.journeybee.io/api-reference/resources/update-a-resource /openapi.json patch /resources/{uuid} # Add a block to a room Source: https://docs.journeybee.io/api-reference/room-blocks/add-a-block-to-a-room /openapi.json post /rooms/{uuid}/blocks # List blocks for a room Source: https://docs.journeybee.io/api-reference/room-blocks/list-blocks-for-a-room /openapi.json get /rooms/{uuid}/blocks # Remove a block from a room Source: https://docs.journeybee.io/api-reference/room-blocks/remove-a-block-from-a-room /openapi.json delete /rooms/{uuid}/blocks/{subUuid} # Update a room block Source: https://docs.journeybee.io/api-reference/room-blocks/update-a-room-block /openapi.json patch /rooms/{uuid}/blocks/{subUuid} # Add a category to a room Source: https://docs.journeybee.io/api-reference/room-categories/add-a-category-to-a-room /openapi.json post /rooms/{uuid}/categories # Remove a category from a room Source: https://docs.journeybee.io/api-reference/room-categories/remove-a-category-from-a-room /openapi.json delete /rooms/{uuid}/categories/{subUuid} # Add a partnership to a room Source: https://docs.journeybee.io/api-reference/room-partnerships/add-a-partnership-to-a-room /openapi.json post /rooms/{uuid}/partnerships # Remove a partnership from a room Source: https://docs.journeybee.io/api-reference/room-partnerships/remove-a-partnership-from-a-room /openapi.json delete /rooms/{uuid}/partnerships/{subUuid} # Add a stage to a room Source: https://docs.journeybee.io/api-reference/room-stages/add-a-stage-to-a-room /openapi.json post /rooms/{uuid}/stages # Remove a stage from a room Source: https://docs.journeybee.io/api-reference/room-stages/remove-a-stage-from-a-room /openapi.json delete /rooms/{uuid}/stages/{subUuid} # Add a tag to a room Source: https://docs.journeybee.io/api-reference/room-tags/add-a-tag-to-a-room /openapi.json post /rooms/{uuid}/tags # Remove a tag from a room Source: https://docs.journeybee.io/api-reference/room-tags/remove-a-tag-from-a-room /openapi.json delete /rooms/{uuid}/tags/{subUuid} # Add a tier to a room Source: https://docs.journeybee.io/api-reference/room-tiers/add-a-tier-to-a-room /openapi.json post /rooms/{uuid}/tiers # Remove a tier from a room Source: https://docs.journeybee.io/api-reference/room-tiers/remove-a-tier-from-a-room /openapi.json delete /rooms/{uuid}/tiers/{subUuid} # Archive a room Source: https://docs.journeybee.io/api-reference/rooms/archive-a-room /openapi.json delete /rooms/{uuid} # Create a room Source: https://docs.journeybee.io/api-reference/rooms/create-a-room /openapi.json post /rooms/ # Get a room Source: https://docs.journeybee.io/api-reference/rooms/get-a-room /openapi.json get /rooms/{uuid} # List rooms Source: https://docs.journeybee.io/api-reference/rooms/list-rooms /openapi.json get /rooms/ # Update a room Source: https://docs.journeybee.io/api-reference/rooms/update-a-room /openapi.json patch /rooms/{uuid} # Create a tag Source: https://docs.journeybee.io/api-reference/tags/create-a-tag /openapi.json post /tags/ # Delete a tag Source: https://docs.journeybee.io/api-reference/tags/delete-a-tag /openapi.json delete /tags/{uuid} # List tags Source: https://docs.journeybee.io/api-reference/tags/list-tags /openapi.json get /tags/ # Update a tag Source: https://docs.journeybee.io/api-reference/tags/update-a-tag /openapi.json patch /tags/{uuid} # Assign a user to a task Source: https://docs.journeybee.io/api-reference/task-users/assign-a-user-to-a-task /openapi.json post /tasks/{uuid}/assigned-users # Remove an assigned user from a task Source: https://docs.journeybee.io/api-reference/task-users/remove-an-assigned-user-from-a-task /openapi.json delete /tasks/{uuid}/assigned-users/{subUuid} # Archive a task Source: https://docs.journeybee.io/api-reference/tasks/archive-a-task /openapi.json delete /tasks/{uuid} # Create a task Source: https://docs.journeybee.io/api-reference/tasks/create-a-task /openapi.json post /tasks/ Create a task optionally attached to a lead or partnership. Content is plain text that gets converted to rich text internally. Assign users by providing their UUIDs. # Get a task Source: https://docs.journeybee.io/api-reference/tasks/get-a-task /openapi.json get /tasks/{uuid} # List tasks Source: https://docs.journeybee.io/api-reference/tasks/list-tasks /openapi.json get /tasks/ List tasks with optional filtering by status, lead, partnership, or search term. Tasks include assigned users and rich text content. # Update a task Source: https://docs.journeybee.io/api-reference/tasks/update-a-task /openapi.json patch /tasks/{uuid} Update task fields. Content is plain text that gets converted to rich text internally. Use the assigned-users sub-resource to manage assignees. # Create a tier Source: https://docs.journeybee.io/api-reference/tiers/create-a-tier /openapi.json post /tiers/ # Delete a tier Source: https://docs.journeybee.io/api-reference/tiers/delete-a-tier /openapi.json delete /tiers/{uuid} # List tiers Source: https://docs.journeybee.io/api-reference/tiers/list-tiers /openapi.json get /tiers/ # Update a tier Source: https://docs.journeybee.io/api-reference/tiers/update-a-tier /openapi.json patch /tiers/{uuid} # List company users Source: https://docs.journeybee.io/api-reference/users/list-company-users /openapi.json get /users/ Returns users in the company. Use the UUID when assigning users to leads, tasks, or partners. # Deal created Source: https://docs.journeybee.io/api-reference/webhook-events/deal-created /openapi.json webhook deals.create Fired when a new deal is created. # Deal deleted Source: https://docs.journeybee.io/api-reference/webhook-events/deal-deleted /openapi.json webhook deals.delete Fired when a deal is deleted. # Deal note created Source: https://docs.journeybee.io/api-reference/webhook-events/deal-note-created /openapi.json webhook deals.notes_create Fired when a note is added to a deal. # Deal note deleted Source: https://docs.journeybee.io/api-reference/webhook-events/deal-note-deleted /openapi.json webhook deals.notes_delete Fired when a deal note is removed. # Deal note updated Source: https://docs.journeybee.io/api-reference/webhook-events/deal-note-updated /openapi.json webhook deals.notes_update Fired when a deal note is updated. # Deal updated Source: https://docs.journeybee.io/api-reference/webhook-events/deal-updated /openapi.json webhook deals.update Fired when a deal is updated. # Lead created Source: https://docs.journeybee.io/api-reference/webhook-events/lead-created /openapi.json webhook leads.create Fired when a new lead is created. # Lead deleted Source: https://docs.journeybee.io/api-reference/webhook-events/lead-deleted /openapi.json webhook leads.delete Fired when a lead is deleted. # Lead note created Source: https://docs.journeybee.io/api-reference/webhook-events/lead-note-created /openapi.json webhook leads.notes_create Fired when a note is added to a lead. # Lead note deleted Source: https://docs.journeybee.io/api-reference/webhook-events/lead-note-deleted /openapi.json webhook leads.notes_delete Fired when a note is removed from a lead. # Lead updated Source: https://docs.journeybee.io/api-reference/webhook-events/lead-updated /openapi.json webhook leads.update Fired when a lead is updated. # Message created Source: https://docs.journeybee.io/api-reference/webhook-events/message-created /openapi.json webhook messages.create Fired when a message is sent. # Partner contact created Source: https://docs.journeybee.io/api-reference/webhook-events/partner-contact-created /openapi.json webhook partners.contact_create Fired when a contact is added to a partner. # Partner contact updated Source: https://docs.journeybee.io/api-reference/webhook-events/partner-contact-updated /openapi.json webhook partners.contact_update Fired when a partner contact is updated. # Partner created Source: https://docs.journeybee.io/api-reference/webhook-events/partner-created /openapi.json webhook partners.create Fired when a new partner is created. # Partner updated Source: https://docs.journeybee.io/api-reference/webhook-events/partner-updated /openapi.json webhook partners.update Fired when a partner is updated. # Authentication Source: https://docs.journeybee.io/guides/authentication How to authenticate with the Journeybee API # Authentication All API requests require an API key sent in the `Authorization` header. ## Getting an API key 1. Log in to [app.journeybee.io](https://app.journeybee.io) 2. Go to **Settings > API Keys** 3. Click **Create API Key** 4. Choose permissions: * **Read** — access to GET endpoints * **Write** — access to POST, PATCH, and DELETE endpoints 5. Copy the key immediately — it won't be shown again ## Using your API key Include the key in the `Authorization` header using either format: ```bash theme={null} # Bearer format (recommended) curl https://api.journeybee.io/v1/partners \ -H "Authorization: Bearer YOUR_API_KEY" # Api-Key format (also supported) curl https://api.journeybee.io/v1/partners \ -H "Authorization: Api-Key YOUR_API_KEY" ``` ## Using your API key with the MCP server The same API key authenticates the [MCP server](/guides/mcp). Point any MCP-capable client (including open-source models, headless runtimes, and CI) at your region's `/mcp` endpoint with `Authorization: Bearer YOUR_API_KEY` — for example `https://mcp.journeybee.io/mcp`. See [Connect with an API key](/guides/mcp#connect-with-an-api-key-open-source-models-headless-ci) for client config. This requires the **API & MCP** module (`api_mcp`) on your company. ## Permissions | Permission | Access | | ---------- | -------------------------------------------------------------------------- | | **Read** | `GET` endpoints — list and retrieve resources | | **Write** | `POST`, `PATCH`, `DELETE` endpoints — create, update, and delete resources | A key with only **Read** permission will receive a `403 Forbidden` response on write operations. ## Rate limits Requests are rate-limited to **500 requests per minute** per API key. Exceeding this returns a `429 Too Many Requests` response with a `Retry-After` header. ## Error responses Missing or invalid API key: ```json theme={null} { "error": { "code": "unauthorized", "message": "Authorization header is required" } } ``` Insufficient permissions: ```json theme={null} { "error": { "code": "forbidden", "message": "Write access is required" } } ``` # Changelog Source: https://docs.journeybee.io/guides/changelog API versioning and recent changes # Changelog ## Versioning The Journeybee API is versioned via the URL path (`/v1/`). Breaking changes will only be introduced in new major versions. Non-breaking additions (new fields, new endpoints) may be added to the current version at any time. ## v1.4.0 ### Custom field enforcement (behavior change) * **Server-side enforcement on every custom field write** — creating or updating a lead, deal, or partner with custom field values (inline, via `POST`/`PATCH`/`DELETE /v1/custom-field-values`, or via the per-entity custom-field endpoints) is now validated against each field's `required` flag, its `partner_type` scope, and any active custom field rules (visibility, locking, auto-population). Previously these writes could silently persist values that violated required/rule/scope constraints. A violation now returns `400 validation_error` with a `details` array — see [Custom field validation](errors.md#custom-field-validation). * **`DELETE` is treated as a blanking submission** — deleting a visible required value returns `required_cannot_blank`; deleting a value locked by an active rule returns `locked_by_rule`. * This is a deliberate behavior change for existing admin-API consumers (the API enforces immediately rather than rolling out gradually) — see the custom field validation guide for the full `reason` code reference. ### Partner field requirements (new endpoints) * **`GET /v1/partner/{partnershipId}/leads/requirements`** and **`GET /v1/partner/{partnershipId}/deals/requirements`** — new read-only endpoints that return the custom fields (with their `required` flag) and active rules a partner's lead/deal submission must satisfy, computed from the exact same logic the write-side validator enforces. See [Field requirements](partner-api.md#field-requirements) in the Partner API guide. * `createPartnerLead`, `updatePartnerLead`, `createPartnerDeal`, and `updatePartnerDeal` now document calling the matching requirements endpoint first, and that validation failures return per-field `details` with machine-readable `reason` codes. ### Lead creation (breaking change) * **`partnership_uuid` is now required on `POST /v1/leads`** — leads always belong to a partnership; campaign leads are created through campaign surfaces (portal campaign forms / public form submissions), not this endpoint. *** ## v1.3.0 ### Audit Log * **Audit Log** — `GET /v1/audit-log` returns a unified, company-scoped activity feed across leads, deals and partnerships. Each entry carries the entity, action (`created` / `updated` / `deleted` / `archived` / `restored`), a rendered actor label, and a `changes` array describing what changed. * **Expanded field coverage** — audit entries now track many more fields per entity: * **Partners** — name, email, phone, website, description, partner type, offline flag, accepted/invited dates, country, currency (in addition to tier, category, stage, archive state). * **Deals** — currency and expiration date (in addition to label, value, commission, stage). * **Leads** — rejected reason, direction, campaign and distributor (in addition to contact fields and status). * **Human-readable values** — foreign-key fields resolve to labels rather than ids (stage, tier, category, country, currency, campaign, distributor), deal amounts are formatted in the deal's currency, and create entries are attributed to the record's creator. *** ## v1.2.0 ### Resources & LMS * **Resources** — Full CRUD with content blocks (text, image, video, document, embed, quiz, and more), tags, categories, and folder organization * **Certifications** — Full CRUD with curriculum management (ordered resources with prerequisites), enrollment tracking, categories, and folders * **Folders** — CRUD for organizing resources and certifications into hierarchical folders ### Rooms * **Rooms** — Full CRUD for partner portal pages with 16 room types and 14 block types (text, video, action bars, banners, goals, pricing calculators, etc.) * **Room access control** — Manage room visibility via tiers, categories, stages, tags, and direct partnership associations ### Webhooks * **Webhook event schemas** — Full typed payload documentation for all 16 webhook events (leads, deals, partners, messages) in the API Reference ### Improvements * **JSONB fields fully typed** — Commission calculations, pricing calculator configs, resource block content, room block settings, and custom field values now show complete structures in the API docs * **UUID validation** — All UUID parameters now validate format (previously accepted any string) * **Asset company scoping** — Asset references now verify company ownership *** ## v1.1.0 ### Developer Tools * **TypeScript SDK** — `@journeybee/sdk` with typed functions for every endpoint. See the [SDK guide](sdk.md). * **MCP Server** — Connect Claude and other AI assistants to Journeybee. See the [MCP guide](mcp.md). * **operationId on all endpoints** — Every endpoint now has a stable `operationId` (e.g., `listLeads`, `createPartner`) for SDK method names and MCP tool names. ### New Endpoints * **Leads** — Full CRUD with sub-resources: tags, assigned users, contacts, custom fields, notes, attributions, distributors, products, quotes, tasks * **Deals** — Full CRUD with sub-resources: tags, notes, products, payments, custom fields, commission, tasks * **Tasks** — Full CRUD with assigned users * **Commissions** — Full CRUD with tier/category/partnership scoping * **Contacts** — List and get across partnerships * **Products** — Full CRUD * **Pricing Calculators** — Full CRUD with fields, products, and formulas * **Attributions** — Full CRUD * **Currencies** — List * **Users** — List company team members *** ## v1.0.0 *Initial release* ### Endpoints * **Partners** — Full CRUD with sub-resources: tags, assigned users, domains, custom fields, contacts, notes, resellers * **Partner Stages** — List, create, update, delete * **Deal Stages** — List, create, update, delete * **Tiers** — List, create, update, delete * **Categories** — List, create, update, delete * **Tags** — List, create, update, delete * **Custom Fields** — List, create, update, delete with option management * **Custom Field Values** — List, create, update, delete ### Authentication * Bearer token and Api-Key authentication * Read/write permission model * Rate limiting (100 requests/minute) # Embedded Lead Forms Source: https://docs.journeybee.io/guides/embedding-forms Embed a Journeybee lead-capture form on any website with a single script tag # Embedded Lead Forms Drop a Journeybee lead-capture form onto any website — your marketing site, a partner microsite, a landing page — with one ` ``` That's the whole integration. The script defines a global `window.journeybee(...)` function; calls you make before the SDK finishes downloading are queued and replayed automatically, so the snippet above works as-is. Your **form UUID** comes from the form's embed settings in Journeybee. It's a public value — safe to ship in client-side HTML. The form is resolved by its UUID alone. ## The `init` call ```js theme={null} window.journeybee("init", formUuid, container, options); ``` | Argument | Type | Required | Description | | ----------- | ------------- | -------- | ---------------------------------------- | | `"init"` | string | yes | The action. | | `formUuid` | string | yes | UUID of your published form. | | `container` | `HTMLElement` | yes | The element the form iframe mounts into. | | `options` | object | no | Configuration (see below). | ### Options | Option | Type | Description | | --------------- | ---------------- | --------------------------------------------------------------------------------------------------------------- | | `companyName` | string | Optional vanity slug used in the form URL path. Cosmetic — the form resolves by UUID. Defaults to `journeybee`. | | `prefill` | object | Values to pre-populate fields with (see [Prefilling](#prefilling-fields)). | | `customization` | object | Theme + layout overrides (see [Styling](#styling-the-form)). | | `onReady` | `() => void` | Fires when the form has mounted and is ready. | | `onSuccess` | `(data) => void` | Fires on a successful submission. | | `onError` | `(data) => void` | Fires on a submission error. | | `onValidation` | `(data) => void` | Fires on real-time field validation changes. | | `debug` | boolean | Logs the postMessage traffic to the console. | ## Styling the form Pass a `customization` object to theme the form. Every value is validated server-side before it can drive any CSS, so only safe values are accepted: * **Colours** must be hex (`#RRGGBB` / `#RGB`), `rgb(...)`, or `rgba(...)`. * **Sizes** must include units (`px`, `rem`, `em`, `%`, `vh`, `vw`). * **Font families** are letters, spaces, commas, and quotes only. ```js theme={null} window.journeybee("init", "YOUR_FORM_UUID", document.getElementById("jb-form"), { customization: { theme: { colors: { primary: "#4f46e5", // buttons, focus rings background: "#ffffff", // form background text: "#111827", // body text }, spacing: { padding: "24px", // padding around the form gap: "16px", // space between fields }, typography: { fontFamily: "'Inter', sans-serif", fontSize: { base: "16px", label: "14px", input: "16px" }, fontWeight: "medium", // normal | medium | semibold | bold }, borders: { radius: "md", // none | sm | md | lg | full | a size value }, layout: { maxWidth: "480px", // caps + centres the form }, }, layout: { submitButton: { text: "Get started" }, }, }, }); ``` ### Supported customization | Path | Values | | ----------------------------- | -------------------------------------------------- | | `theme.colors.primary` | colour — buttons, focus rings | | `theme.colors.background` | colour — form background | | `theme.colors.text` | colour — body text | | `theme.spacing.padding` | size — padding around the form | | `theme.spacing.gap` | size — vertical space between fields | | `theme.typography.fontFamily` | font stack | | `theme.typography.fontSize.*` | `base`, `label`, `input` sizes | | `theme.typography.fontWeight` | `normal` \| `medium` \| `semibold` \| `bold` | | `theme.borders.radius` | `none` \| `sm` \| `md` \| `lg` \| `full` \| a size | | `theme.layout.maxWidth` | size — caps and centres the form | | `layout.submitButton.text` | submit button label | ## Prefilling fields Pass a `prefill` object to pre-populate fields. Standard fields are keyed by name; custom fields are keyed by their field UUID: ```js theme={null} window.journeybee("init", "YOUR_FORM_UUID", document.getElementById("jb-form"), { prefill: { email: "jane@acme.com", first_name: "Jane", last_name: "Smith", company_name: "Acme", phone_number: "+1 555 0100", partnership_uuid: "PARTNERSHIP_UUID", // custom field, keyed by its UUID: "b1d2…": "Enterprise", }, }); ``` Prefill is applied to standard fields and to **text / number** custom fields. Empty values are ignored. ## Handling submissions Use the callbacks to react to submissions — close a modal, redirect, fire analytics, etc. ```js theme={null} window.journeybee("init", "YOUR_FORM_UUID", document.getElementById("jb-form"), { onReady: () => console.log("form ready"), onSuccess: (data) => { // lead captured in Journeybee window.location.href = "/thank-you"; }, onError: (data) => console.error("submission failed", data), debug: true, }); ``` ## React Load the loader once, initialize on mount, and tear down on unmount so you never leak a duplicate iframe: ```tsx theme={null} import { useEffect, useRef } from "react"; const FORM_UUID = "YOUR_FORM_UUID"; const FORMS_BASE_URL = "https://forms.journeybee.io"; export function LeadForm() { const ref = useRef(null); useEffect(() => { const SCRIPT_ID = "journeybee-forms-loader"; let script = document.getElementById(SCRIPT_ID) as HTMLScriptElement | null; const init = () => { if (!ref.current || !window.journeybee) return; window.journeybee("init", FORM_UUID, ref.current, { customization: { theme: { colors: { primary: "#4f46e5" } } }, onSuccess: (data) => console.log("submitted", data), }); }; if (!script) { script = document.createElement("script"); script.id = SCRIPT_ID; script.src = `${FORMS_BASE_URL}/api/embed`; script.async = true; script.addEventListener("load", init); document.body.appendChild(script); } else if (window.journeybee) { init(); } return () => { window.journeybee?.("destroy", FORM_UUID); if (ref.current) ref.current.innerHTML = ""; }; }, []); return
; } ``` ## Modal pattern The form mounts into whatever container you give it, so a modal is just a container that appears on demand: ```js theme={null} function openFormModal() { document.getElementById("modal").style.display = "block"; window.journeybee("init", "YOUR_FORM_UUID", document.getElementById("modal-body"), { onSuccess: () => (document.getElementById("modal").style.display = "none"), }); } ``` ## Tearing down Remove a mounted form (e.g. when closing a modal or unmounting a component): ```js theme={null} window.journeybee("destroy", "YOUR_FORM_UUID"); ``` To re-render with new options (a different theme or prefill), `destroy` then `init` again. ## Security * **Public by design.** Form UUID and company slug are safe in client HTML — the form is a public, unauthenticated landing surface and can be embedded on any origin. * **Customization is sandboxed.** Colours, sizes, and fonts are regex-validated server-side; arbitrary CSS cannot be injected. * **Spam protection** is built in: a hidden honeypot field and a minimum time-to-submit silently drop automated submissions. ## Troubleshooting | Symptom | Likely cause | | -------------------------------- | ------------------------------------------------------------------------------- | | Form doesn't appear | Wrong/unpublished form UUID, or the container element doesn't exist yet. | | Customization has no effect | A value failed validation (e.g. a colour without `#`, or a size without units). | | Prefill doesn't populate a field | Empty value, or a custom field that isn't a text/number type. | | `onSuccess` never fires | Check the browser console with `debug: true` for the postMessage traffic. | # Embedded Partner Portal Source: https://docs.journeybee.io/guides/embedding-portal Embed the authenticated partner portal inside your own app with a backend-minted SSO token # Embedded Partner Portal Drop your partner's **real portal** — their rooms, deals, resources, contacts, everything — straight into your own app, inside an iframe. The partner is signed in automatically with a short-lived token your backend mints, so there's no second login. The frame auto-resizes to its content and reports navigation back to your page. This is **not** the same as [embedding lead forms](/guides/embedding-forms). Lead forms are a public, unauthenticated capture surface loaded with a script tag and a form UUID. The partner portal is an **authenticated** product surface: it requires an SSO token minted by your backend, the [`@journeybee/embed`](https://www.npmjs.com/package/@journeybee/embed) SDK, and an allowlisted host. The two are completely separate integrations. Embedding requires **HTTPS** on both the host page and the portal. The portal session cookie is `SameSite=None; Secure`, which browsers refuse to set on insecure origins. Use a tunnel (e.g. ngrok) for local testing. Prefer a running example? The [**Journeybee Embed Examples**](https://github.com/Journeybeeio/embed-examples) repo is a live playground + copy-paste snippets (forms + portal), including a backend token-mint reference — clone it, `pnpm install`, `pnpm dev`. ## How it works 1. Your backend authenticates the user (you already do this) and mints a short-lived, single-use **SSO token** signed with your embed signing key. 2. The `@journeybee/embed` SDK mounts an iframe pointing at the portal, carrying your public **company id** and the token. 3. The portal verifies the token server-side, establishes a session, and hands off to the partner's portal — signed in, in embed mode. 4. The SDK wires a postMessage bridge: auto-resize, navigation events, sign-out. **You don't pick a page.** The embed lands on the portal and the portal decides what to show: a partner with one partnership lands straight in it, with several gets a picker. The embed's only job is to authorise your app and sign the user in — everything after that is the portal's own logic. The tenant is identified by your **public company id** (`companies.uuid`), not by the URL the portal is served from. **Embedding does not require a custom domain** — it works on the shared `portals.journeybee.io` or your own custom domain, independently. ## Prerequisites (one-time, in Journeybee) 1. Enable the **Embedded Portal** module — Settings → Portal Settings. 2. Copy your **company id** (the public `companies.uuid` shown there). You pass it to the SDK as `company` and set it as the token's `iss`/`aud`. 3. Add the **hostname of the page that will host the iframe** to **Embedded Portal Domains** (e.g. `app.acme.com`). The portal refuses to render in a frame whose ancestor isn't allowlisted (`frame-ancestors` CSP). 4. Create an **Embed Signing Key** under **Embed Signing Keys**. The secret (`jb_embed_…`) is shown **once** — store it as a server-side secret. These are three independent settings. **Embedded Portal Domains** (who may iframe the portal) is *not* the same as the portal **Custom Domain** (a branded URL for top-level access) — embedding works with neither, either, or both. ## Install Published on npm — [**`@journeybee/embed`**](https://www.npmjs.com/package/@journeybee/embed): ```bash theme={null} npm install @journeybee/embed ``` ## Quick start ### 1. Mint a token on your backend Run this on your server, **after** you've authenticated the user. Never mint a token in the browser — anyone with the signing key can impersonate any user. ```ts theme={null} // Node backend — e.g. an Express / Next.js API route import jwt from "jsonwebtoken"; import { randomUUID } from "node:crypto"; const EMBED_SIGNING_SECRET = process.env.JOURNEYBEE_EMBED_SECRET!; // jb_embed_… const COMPANY = "YOUR_COMPANY_ID"; // your public company id (companies.uuid) export function mintPortalEmbedToken(user: { email: string }) { const now = Math.floor(Date.now() / 1000); return jwt.sign( { iss: COMPANY, // issuer — your public company id (companies.uuid) sub: user.email, // the user to sign in (an existing portal user, or JIT) aud: COMPANY, // audience — same id; binds the token to this tenant // partnership_uuid: "…", // optional explicit partnership target (JIT) iat: now, exp: now + 60, // short-lived — the portal enforces exp ≤ iat + 120s jti: randomUUID(), // unique — single-use, replays are rejected }, EMBED_SIGNING_SECRET, { algorithm: "HS256" }, ); } ``` Only the **public company id** (`companies.uuid`) ever appears in the token or in client code. Journeybee's internal numeric ids are never exposed. ### 2. Mount the embed on the client ```ts theme={null} import { createPortalEmbed } from "@journeybee/embed"; // Fetch a token from YOUR backend (above). Never mint it in the browser. const token = await fetch("/api/portal-embed-token", { method: "POST" }).then( (r) => r.text(), ); const embed = createPortalEmbed({ target: "#portal", // element or selector to mount into portalHost: "portals.journeybee.io", // where the portal is served (or your custom domain) company: "YOUR_COMPANY_ID", // your public company id (companies.uuid) token, // exchanged for a session on first load }); embed.on("portal:navigate", (msg) => console.log("navigated to", msg.path)); // Later, to tear it down: // embed.destroy(); ``` That's the whole integration. `portalHost` is just **where** the portal is served; `company` identifies **which** tenant — they're independent. ## The `createPortalEmbed` call ```ts theme={null} createPortalEmbed(options): PortalEmbed ``` | Option | Type | Required | Notes | | ------------ | ------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `target` | `HTMLElement \| string` | yes | Element or selector to mount the iframe into. | | `portalHost` | `string` | yes | Where the portal is served, e.g. `portals.journeybee.io`. `https` assumed. | | `company` | `string` | yes | Your public company id (`companies.uuid`). | | `token` | `string` | no\* | Backend-minted SSO token. \*Without a valid session the embed shows a "session required" state and emits `portal:authRequired`. | | `init` | `{ locale?: string; theme?: string }` | no | Initial config forwarded to the portal via `host:init`. | | `title` | `string` | no | iframe `title` for accessibility. | | `autoResize` | `boolean` | no | Default `true`: grow the iframe to fit the portal's content. Set `false` to **fill the mount container** (`height: 100%`, portal scrolls internally) — give the container a definite height. Recommended when mounting the portal in a fixed app pane. | It returns a `PortalEmbed`: | Member | Description | | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `iframe` | The created `HTMLIFrameElement`. | | `on(type, listener)` | Subscribe to a portal event. Returns an unsubscribe fn. | | `signOut()` | End the portal session, then emit `portal:authRequired` (the frame shows a "session ended" state — it never navigates to a login page). Handle re-auth/unmount from that event. | | `destroy()` | Remove the iframe + listeners (host-side teardown). Does **not** clear the session — re-mounting reuses it. For a full logout: `signOut()` then `destroy()`. | ## Events Subscribe with `embed.on(type, listener)`: | Event | Payload | Meaning | | ---------------------- | ------------ | --------------------------------------------------- | | `portal:ready` | — | Bridge is up; the host may send `init`. | | `portal:resize` | `{ height }` | The SDK auto-resizes the iframe for you. | | `portal:authRequired` | `{ reason }` | No session — mint a fresh token and re-mount. | | `portal:authenticated` | — | A session now exists (bare signal — never a token). | | `portal:navigate` | `{ path }` | The portal's internal route changed. | ## React ```tsx theme={null} import { createPortalEmbed, type PortalEmbed } from "@journeybee/embed"; import { useEffect, useRef } from "react"; export function PartnerPortal() { const ref = useRef(null); useEffect(() => { let embed: PortalEmbed | undefined; let cancelled = false; (async () => { const token = await fetch("/api/portal-embed-token", { method: "POST", }).then((r) => r.text()); if (cancelled || !ref.current) return; embed = createPortalEmbed({ target: ref.current, portalHost: "portals.journeybee.io", company: "YOUR_COMPANY_ID", token, }); })(); return () => { cancelled = true; embed?.destroy(); }; }, []); return
; } ``` ## Re-authentication A token is single-use and short-lived; the resulting session lasts much longer. When the session is missing or expired, the embed emits `portal:authRequired` — mint a fresh token and re-mount: ```ts theme={null} embed.on("portal:authRequired", async () => { const token = await fetch("/api/portal-embed-token", { method: "POST" }).then( (r) => r.text(), ); embed.destroy(); embed = createPortalEmbed({ target: "#portal", portalHost, company, token }); }); ``` ## Security model * **No secrets cross the iframe boundary.** `portal:authenticated` is a bare signal — never a token or session. The session lives in an `HttpOnly` cookie the portal sets itself. * **Tokens are backend-minted only**, short-lived (`exp ≤ iat + 120s`), single-use (`jti` replay-guarded), and audience/issuer-bound to your company id. The signing secret never reaches the browser. * **Origin allowlisting** is enforced server-side: the portal only frames inside hosts you've added to **Embedded Portal Domains**. * **Public ids only.** The token and all client code use the public `companies.uuid`; internal numeric ids are never exposed. * **Storage Access.** In browsers that partition third-party storage (e.g. Safari), the portal prompts once for Storage Access so its session cookie is delivered inside the iframe. The SDK marks the iframe `allow="storage-access"`. ## Troubleshooting | Symptom | Likely cause | | ----------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | | Frame refuses to load / `frame-ancestors` error | The host page's origin isn't in **Embedded Portal Domains**, or it isn't HTTPS. | | "Session required" gate keeps showing | No/expired token, or the token failed verification (wrong key, `iss`/`aud` ≠ company id, expired, or replayed). | | Session not delivered (Safari) | Storage Access wasn't granted — the portal shows an "Enable secure session" prompt on a click. | | Cookie not set at all | Host page or portal isn't HTTPS — `SameSite=None; Secure` cookies need a secure origin. | # Error Handling Source: https://docs.journeybee.io/guides/errors Error codes and handling in the Journeybee API # Error Handling The API uses standard HTTP status codes and returns errors in a consistent JSON format. ## Error response format ```json theme={null} { "error": { "code": "not_found", "message": "Partner not found" } } ``` ## Error codes | HTTP Status | Code | Description | | ----------- | --------------------- | ----------------------------------------------------------------------- | | `401` | `unauthorized` | Missing or invalid API key | | `403` | `forbidden` | API key lacks required permission (read or write) | | `403` | `module_disabled` | The requested module is not enabled for your account | | `404` | `not_found` | The requested resource does not exist | | `409` | `conflict` | The action conflicts with existing data (e.g., deleting a stage in use) | | `400` | `validation_error` | Request body or query parameters failed validation | | `429` | `rate_limit_exceeded` | Too many requests — wait and retry | | `500` | `internal_error` | Unexpected server error | ## Handling errors Check the HTTP status code first, then use `error.code` for programmatic handling: ```javascript theme={null} const response = await fetch("https://api.journeybee.io/v1/partners", { method: "POST", headers: { Authorization: "Bearer YOUR_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ name: "Acme Corp" }), }); if (!response.ok) { const { error } = await response.json(); switch (error.code) { case "unauthorized": // Re-authenticate or check API key break; case "validation_error": // Fix request body based on error.message break; case "rate_limit_exceeded": // Wait for Retry-After header duration break; default: // Log and retry or alert break; } } ``` ## Validation errors When request validation fails, the `message` field describes which fields are invalid: ```json theme={null} { "error": { "code": "validation_error", "message": "body/name: Required" } } ``` ## Conflict errors Delete operations on configuration resources (stages, tiers, categories, tags, custom fields) return `409 Conflict` if the resource is currently in use: ```json theme={null} { "error": { "code": "conflict", "message": "Stage \"Active\" is in use by 12 record(s) and cannot be deleted" } } ``` Reassign the records to a different stage/tier/category before retrying the delete. ## Custom field validation Creating or updating a lead, deal, or partner with custom field values — whether inline on the entity itself, or via `POST`/`PATCH`/`DELETE /v1/custom-field-values` and the per-entity custom-field endpoints (`PUT`/ `PATCH`/`DELETE /v1/leads/:uuid/custom-fields`, `.../deals/:uuid/custom-fields`, `.../partners/:uuid/custom-fields`) — is validated server-side against each field's `required` flag, its `partner_type` scope, and any active custom field rules (visibility, locking, auto-population). A violation returns a `400` `validation_error` with a `details` array — one entry per rejected field: | Field | Description | | --------- | ------------------------------------------------------------- | | `field` | The custom field's UUID | | `label` | The custom field's label (omitted when it can't be disclosed) | | `reason` | A machine-readable reason code (see below) | | `message` | A human-readable explanation | `reason` is one of: | Reason | Meaning | | ----------------------- | ------------------------------------------------------------------------------ | | `missing_required` | A required field (visible under current rule state) was not supplied on create | | `required_cannot_blank` | An update attempted to clear a required field that currently has a value | | `hidden_by_rule` | A value was submitted for a field a rule currently hides | | `locked_by_rule` | A value was submitted for a field a rule locks to a different value | | `not_accessible` | The field exists but isn't available on this surface | | `unknown_field` | No custom field matches the given UUID | | `invalid_value` | The submitted value doesn't match the field's type | | `invalid_option` | A `select`/`multi_select` value references an option that doesn't exist | ### Deleting a custom field value `DELETE` is treated as submitting an empty value for the field, so the same checks apply: * Deleting a **visible, required** value returns `required_cannot_blank` — clearing it would leave the entity without a value a required field demands. * Deleting a value that's currently **locked by an active custom field rule** returns `locked_by_rule` — the value can't be changed (including to empty) while the rule that sets it is active. * Deleting a **hidden** field's value, or a value with no active constraints, succeeds normally (`204`). Example response (`400`): ```json theme={null} { "error": { "code": "validation_error", "message": "\"Country\" is required; \"Renewal Terms\" is only visible when \"Deal Type\" is \"Enterprise\"", "details": [ { "field": "b3f1c2a0-1111-4a2b-9c3d-000000000001", "label": "Country", "reason": "missing_required", "message": "\"Country\" is required" }, { "field": "b3f1c2a0-2222-4a2b-9c3d-000000000002", "label": "Renewal Terms", "reason": "hidden_by_rule", "message": "\"Renewal Terms\" is only visible when \"Deal Type\" is \"Enterprise\"" } ] } } ``` # Idempotency Source: https://docs.journeybee.io/guides/idempotency Safely retry POST requests without creating duplicate resources # Idempotency Every `POST` request to the Journeybee API must include a unique `Idempotency-Key` header. Retries with the same key return the cached response from the first successful attempt, so a network blip or client retry never creates a duplicate lead, deal, or subscription. ## Quick start Generate a UUID per logical operation and send it with your request: ```bash theme={null} curl -X POST https://api.journeybee.io/v1/leads \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Idempotency-Key: 7f3a9b2c-4e8d-4a5b-9c1d-8e5f2a3b4c5d" \ -H "Content-Type: application/json" \ -d '{ "first_name": "Jane", "email": "jane@acme.test", "partnership_uuid": "" }' ``` If the network times out and you retry with the **same key and body**, you get the same response — no duplicate lead is created. ## Rules | Condition | Response | | ---------------------------------------------- | ------------------------------------------------ | | First request with a new key | Runs the handler, caches the response for 24h | | Retry with same key + same body | Replays the cached response (same status + body) | | Retry with same key + **different** body | `422` — key was reused for a different request | | Retry while the first request is still running | `409` — tells you to back off and retry | | 5xx failure (server error) | Not cached — your retry gets a fresh attempt | | Missing header on any POST | `400` — header is required | ## Key format * 1–255 characters * Alphanumeric, underscore, hyphen (`[A-Za-z0-9_-]`) * UUIDs recommended — they're collision-free and easy to generate * Keys are scoped per company, so two companies using the same key string will not collide ## Retention Each key's response is cached for **24 hours**. After that, the same key becomes fresh again. ## What to use as a key Pick a value that uniquely identifies the **logical** operation the client intends, not the physical HTTP request: * ✅ `crm-lead-7841-sync` — one key per lead you're syncing. * ✅ A UUID generated when the user clicked "Save". * ❌ `Date.now()` — changes on every retry, defeats dedup. * ❌ A constant string — every request collides. ## Client examples ### Node.js ```javascript theme={null} import { randomUUID } from "node:crypto"; async function createLead(payload) { const key = randomUUID(); for (let attempt = 0; attempt < 3; attempt++) { const res = await fetch("https://api.journeybee.io/v1/leads", { method: "POST", headers: { Authorization: `Bearer ${process.env.JOURNEYBEE_API_KEY}`, "Idempotency-Key": key, "Content-Type": "application/json", }, body: JSON.stringify(payload), }); if (res.status === 409) { // in flight — back off and retry with the SAME key await new Promise((r) => setTimeout(r, 500 * 2 ** attempt)); continue; } return res.json(); } throw new Error("createLead: exhausted retries"); } ``` ### Python ```python theme={null} import uuid import time import requests def create_lead(payload, api_key): key = str(uuid.uuid4()) for attempt in range(3): r = requests.post( "https://api.journeybee.io/v1/leads", headers={ "Authorization": f"Bearer {api_key}", "Idempotency-Key": key, }, json=payload, ) if r.status_code == 409: time.sleep(0.5 * 2 ** attempt) continue return r.json() raise Exception("exhausted retries") ``` ## Scope today Idempotency is required on **all `POST` endpoints**. `PATCH` and `DELETE` are not currently required to carry the header — most `PATCH` requests are naturally idempotent (setting a field to the same value twice has the same effect as once) and `DELETE` is idempotent by definition. ## Related * [Authentication](/guides/authentication) — API key setup * [Errors](/guides/errors) — error response shapes # MCP Server Source: https://docs.journeybee.io/guides/mcp Connect AI assistants to Journeybee via the Model Context Protocol # MCP Server The Journeybee MCP server lets AI assistants like Claude manage your partners, leads, deals, and more through the [Model Context Protocol](https://modelcontextprotocol.io). There are two ways to connect, both to the same per-region URL — pick by client: * **OAuth 2.1** — for interactive clients (Claude, ChatGPT, Cursor). Sign in and approve access; no install, no API keys, nothing to run locally. * **API key** — for open-source models, headless runtimes (Ollama, Llama, Mistral, LangChain), and CI. Any MCP-capable client points at the region URL with an `Authorization: Bearer` header. See [Connect with an API key](#connect-with-an-api-key-open-source-models-headless-ci). ## Connect the hosted server (OAuth) The hosted server runs **one endpoint per region** and authenticates with **OAuth 2.1** — you log in with your normal Journeybee account and approve access, so there are no API keys to copy. Use the URL for the region your company's data lives in: | Region | MCP URL | | -------------- | ---------------------------------- | | Europe (EU) | `https://mcp.journeybee.io/mcp` | | Australia (AU) | `https://au.mcp.journeybee.io/mcp` | 1. In your AI client (Claude, ChatGPT, or any MCP-compatible client), add a **remote MCP server / custom connector** with **your region's URL** from the table above. 2. The client opens Journeybee's sign-in page. Log in as usual. 3. On the **consent screen**, choose which company the connection may access and click **Allow**. 4. You're returned to your client, connected. Tokens refresh automatically; revoke access any time by removing the connector. Access is **scoped to the one company** you pick at consent, with the same permissions your account has. Nothing is shared across tenants — and your data never leaves its region. ## Connect with an API key (open-source models, headless, CI) Open-source models and headless runtimes can't do the browser sign-in, so they connect with a Journeybee **API key** instead of OAuth. The endpoint is the same — the API key is just sent as the Bearer token. **Requirements** * The **API & MCP** module (`api_mcp`) enabled on your company. * An API key from **Settings > API Keys** in [app.journeybee.io](https://app.journeybee.io) (see [Authentication](/guides/authentication)). Use a **Read** key for read-only access; a **Write** key to create and update. **Configure your client** Point any MCP-capable client at your region's `/mcp` endpoint over Streamable HTTP, with the key in the `Authorization` header. Most clients accept a config like this: ```json theme={null} { "mcpServers": { "journeybee": { "url": "https://mcp.journeybee.io/mcp", "headers": { "Authorization": "Bearer YOUR_API_KEY" } } } } ``` Use the `/mcp` path on **your region's** host from the table above (`https://au.mcp.journeybee.io/mcp` for Australia). The same key formats the REST API accepts work here — `Authorization: Bearer YOUR_API_KEY` or `Authorization: Api-Key YOUR_API_KEY`. To check connectivity without a client, list the tools directly: ```bash theme={null} curl -sS https://mcp.journeybee.io/mcp \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}' ``` Same guarantees as OAuth: the key is **scoped to one company**, carries exactly its own **Read/Write** permission (a read-only key never sees write tools), and reaches **only its own region**. The only difference is how you obtain the token — a pre-issued key instead of an interactive consent screen. ## Architecture The MCP server is organized into four layers: | Layer | Count | Purpose | | ------------------- | ----- | ------------------------------------------------------------------------- | | **Composite tools** | 20 | High-level, outcome-oriented operations (1 tool call = complete workflow) | | **Discovery tools** | 3 | Browse, inspect, and execute any of 207+ API endpoints on demand | | **Resources** | 17 | Read-only configuration data (no tool call needed) | | **Prompts** | 10 | Guided workflow templates | **Total: 23 tools** — all 207 API endpoints accessible via discovery, common operations optimized with composites. ## Composite tools Start here. Composites combine multiple API calls into a single, high-level operation with formatted markdown output. ### Overview tools (get full entity context) | Tool | Description | | -------------------------- | -------------------------------------------------------------------- | | `getLeadOverview` | Lead + notes, tasks, products, contacts, attributions, custom fields | | `getDealOverview` | Deal + notes, products, commission, payments, tasks, custom fields | | `getPartnerOverview` | Partner + contacts, notes, custom fields, domains | | `getResourceOverview` | Resource + content blocks | | `getCertificationOverview` | Certification + curriculum, enrollments | | `getRoomOverview` | Room + content blocks | ### Analytics & search | Tool | Description | | ----------------------- | -------------------------------------------------------------------------------------------- | | `getPipelineSummary` | Deal stages with counts and values (currency-converted, filterable by date and partner type) | | `searchEverything` | Search partners, leads, and deals by keyword in one call | | `getCommissionReport` | All commission rules with tier/category assignments | | `getPartnerCommissions` | Commission rules applicable to a specific partner | | `whoami` | Account info, team size, partner count | ### Workflow tools (multi-step operations) | Tool | Description | | --------------------------- | ------------------------------------------------- | | `createLeadWithDetails` | Create lead + add tags + assign users | | `createPartnerWithContacts` | Create partner + add contacts | | `updatePartnerDetails` | Update partner fields + custom fields in one call | | `assignTaskToPartner` | Create a task linked to a partner | | `createResourceWithBlocks` | Create resource + add content blocks | | `buildRoom` | Create room + add content blocks | ### Access control tools | Tool | Description | | -------------------------------- | ----------------------------------------------------------------------- | | `getRoomAccessSettings` | See which tiers, categories, stages, and partnerships can access a room | | `getResourceAccessSettings` | See which partner types can access a resource | | `getCertificationAccessSettings` | See certification access settings | ## Discovery tools For any operation not covered by composites, use the 3 discovery tools to access all 207+ API endpoints: ### `list_endpoints` Browse all available API endpoints grouped by domain. ``` list_endpoints() # All endpoints list_endpoints({ domain: "Leads" }) # Just lead endpoints ``` ### `get_endpoint_schema` Get the full parameter schema for any endpoint before calling it. ``` get_endpoint_schema({ endpoint: "addLeadTag" }) ``` Returns: parameter names, types, required/optional, descriptions. ### `call_endpoint` Execute any endpoint by operationId. ``` call_endpoint({ endpoint: "addLeadTag", args: { uuid: "lead-uuid", tag_uuid: "tag-uuid" } }) ``` ### Naming conventions OperationIds follow predictable patterns: | Pattern | Example | | ---------- | ------------------------------------------- | | `listX` | `listPartners`, `listLeads`, `listDeals` | | `getX` | `getPartner`, `getLead`, `getDeal` | | `createX` | `createPartner`, `createLead`, `createDeal` | | `updateX` | `updatePartner`, `updateLead`, `updateDeal` | | `deleteX` | `deletePartner`, `deleteLead`, `deleteDeal` | | `addXY` | `addLeadTag`, `addPartnerDomain` | | `removeXY` | `removeLeadTag`, `removePartnerDomain` | ## Resources Resources provide read-only configuration data that the AI can reference without making tool calls. Access them through the MCP client's resource browser. ### Dynamic resources (fetched from API) | Resource | Description | | ---------------- | ---------------------------------------- | | Pipeline Summary | Deal stages with counts and values | | Partner Stages | Partner pipeline stages | | Deal Stages | Deal pipeline stages | | Tiers | Partner tiers | | Categories | Partner categories | | Tags | All tags (lead, partner, deal, etc.) | | Custom Fields | Field definitions with types and options | | Team Members | Company users with roles | | Currencies | Available currencies | | Commission Rules | Commission calculations and scoping | | Attributions | Lead attribution types | ### Static resources (enum values) | Resource | Values | | -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | | Partner Types | referral, reseller, distributor | | Lead Statuses | new, converted, rejected | | Deal Phases | new, in\_progress, won, lost, expired, on\_hold | | Resource Block Types | text, image, video, document, button, embed, powerpoint, files, quiz | | Room Block Types | text, image, video, document, embed, introduction, action\_bar, banner, lead\_deal\_analytics, goals, link, advanced\_search, files, pricing\_calculator | | Room Types | overview, leads, deals, payments, resources, certifications, projects, contacts, messages, custom, and more | ## Prompts Select a prompt template from the MCP client for guided workflows: * **Analyze my pipeline** — deal distribution, revenue forecasting * **Partner quarterly review** — partner performance summary * **Set up a new partner** — step-by-step partner creation * **Create a learning path** — certification with resources * **Find and update** — search + modify entities * **Commission report** — commission rules analysis * **Bulk update leads** — batch lead modifications * **Build a portal room** — room creation with blocks * **Check access settings** — room/resource access audit * **Manage tasks** — task creation and assignment ## Error handling When operations fail, the MCP returns actionable error messages: * **Validation errors** list the specific fields that are wrong and suggest using `get_endpoint_schema` * **Not found errors** suggest using `searchEverything` to find the entity * **Conflict errors** explain what's blocking the operation (e.g., "Stage in use by 12 deals") * **Auth errors** indicate whether your session is missing/invalid or lacks write permission — reconnect or re-authorize the connector * **Module-disabled errors** mean the company's **API & MCP** access (`api_mcp`) is off (every call fails) or a specific feature module is disabled — contact your admin ## Markdown support All text content fields accept **markdown**. Use markdown for: * **Notes** (lead, deal, partner) — the `content` field * **Tasks** — the `content` field * **Resource text blocks** — use the `markdown` field * **Room text blocks** — use the `markdown` field Markdown is converted to rich text automatically. Supports headings, bold, italic, lists, links, code blocks, blockquotes. ## Security * **OAuth 2.1** (Authorization Code + PKCE). Each request carries the caller's own short-lived token — the server never holds a shared cross-tenant key. Tokens are scoped to the single company chosen at consent and refresh-rotate; revoke by removing the connector. * **API keys** get the same per-request, company-scoped, region-resident guarantees — the only difference is how the token is obtained (a pre-issued key versus an OAuth consent screen), not what it can reach. The server forwards your key to the same regional API as the SDK; it stores no key and shares nothing across tenants. * **Account permissions apply.** The connection has exactly the permissions your Journeybee account has — a read-only account can only reach read tools. * Access requires the **API & MCP** module (`api_mcp`) on your company — enforced on every `/v1` call. * **Region-resident.** Each region runs its own isolated endpoint; you connect your region's URL and it only reaches that region's API and database. A company's data never crosses regions. * All data is scoped to your company; the MCP server calls the same public API as the SDK — no additional access, no internal-only tools exposed. * Tool annotations indicate read-only, destructive, and idempotent operations. # Pagination Source: https://docs.journeybee.io/guides/pagination How pagination works in the Journeybee API # Pagination List endpoints return paginated results using offset-based pagination. ## Query parameters | Parameter | Type | Default | Description | | ---------- | ------- | ------- | ------------------------ | | `page` | integer | `1` | Page number (1-indexed) | | `per_page` | integer | `25` | Items per page (max 100) | ## Response format Paginated responses include a `pagination` object alongside the `data` array: ```json theme={null} { "data": [ { "uuid": "abc-123", "name": "Acme Corp" } ], "pagination": { "page": 1, "per_page": 25, "total": 42, "total_pages": 2 } } ``` ## Example Fetch the second page with 10 items per page: ```bash theme={null} curl "https://api.journeybee.io/v1/partners?page=2&per_page=10" \ -H "Authorization: Bearer YOUR_API_KEY" ``` ## Iterating through all results Increment `page` until it exceeds `total_pages`: ```javascript theme={null} let page = 1; let totalPages = 1; do { const response = await fetch( `https://api.journeybee.io/v1/partners?page=${page}&per_page=100`, { headers: { Authorization: "Bearer YOUR_API_KEY" } }, ); const { data, pagination } = await response.json(); // Process data... totalPages = pagination.total_pages; page++; } while (page <= totalPages); ``` ## Non-paginated endpoints Some list endpoints (stages, tiers, categories, tags, custom fields) return all records without pagination since these collections are typically small. # Partner API Source: https://docs.journeybee.io/guides/partner-api Build company-scoped partner integrations with OAuth or API keys. # 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 ``` https://api.journeybee.io/v1 ``` ## Authentication Partner connections use either OAuth or a partner API key. Send the credential in the `Authorization` header: ```text theme={null} Authorization: Bearer ``` 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](/guides/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: ```bash theme={null} curl https://api.journeybee.io/v1/partner/me \ -H "Authorization: Bearer YOUR_CREDENTIAL" ``` The response includes your user identity, your read/write permissions, and the partnerships available to you — each with a `uuid`, `name`, `partner_type`, and `status`. That `uuid` is the `{partnershipId}` you send on every other request (see [Company and partnership scope](#company-and-partnership-scope) below). ## Examples List deals for a partnership: ```bash theme={null} curl https://api.journeybee.io/v1/partner/{partnershipId}/deals \ -H "Authorization: Bearer YOUR_CREDENTIAL" ``` Create a lead: ```bash theme={null} curl -X POST https://api.journeybee.io/v1/partner/{partnershipId}/leads \ -H "Authorization: Bearer YOUR_CREDENTIAL" \ -H "Content-Type: application/json" \ -d '{ "first_name": "Jane", "last_name": "Doe", "email": "jane@example.com" }' ``` ## 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: ```text theme={null} /v1/partner/{partnershipId}/leads /v1/partner/{partnershipId}/deals ``` 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: ```json theme={null} { "data": [], "pagination": { "page": 1, "per_page": 25, "total": 42, "total_pages": 2 } } ``` Errors follow a single envelope: ```json theme={null} { "error": { "code": "not_found", "message": "Lead not found" } } ``` `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: ``` GET /v1/partner/{partnershipId}/leads/requirements GET /v1/partner/{partnershipId}/deals/requirements ``` The response lists the custom fields available to this partnership — with each field's `required` flag — and the active rules that apply to them: ```json theme={null} { "fields": [ { "id": "b3f1c2a0-1111-4a2b-9c3d-000000000001", "label": "Deal Type", "type": "select", "required": false, "options": [{ "id": 10, "label": "Enterprise" }] }, { "id": "b3f1c2a0-2222-4a2b-9c3d-000000000002", "label": "Renewal Terms", "type": "text", "required": true, "options": null } ], "rules": [ { "id": "b3f1c2a0-3333-4a2b-9c3d-000000000003", "label": "Show renewal terms for Enterprise", "action_type": "show_field", "source_field": "b3f1c2a0-1111-4a2b-9c3d-000000000001", "source_values": [10], "target_field": "b3f1c2a0-2222-4a2b-9c3d-000000000002", "target_value": null, "value_mappings": null, "locked": false } ] } ``` `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 Enterprise" rule keeps it visible. 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 whose source or target field isn't visible to a partner is left out entirely, 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](/guides/errors#custom-field-validation) for the full reason code reference. ## 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. ## 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. # Partner MCP Source: https://docs.journeybee.io/guides/partner-mcp Connect an AI client to your partnership data via the Model Context Protocol. # Partner MCP The partner MCP server lets AI clients — Claude, ChatGPT, Cursor, and other [Model Context Protocol](https://modelcontextprotocol.io) clients — work with your partnership's leads, deals, and more in natural language. It calls the same [Partner API](/guides/partner-api) endpoints, so the same authentication, scoping, and visibility rules apply. ## What you can do A connected client can check who you are and list your partnerships, read and register leads, and read, create, and update deals — including moving them between pipeline stages. Every operation is scoped to the partnerships you can access and follows the portal's visibility rules; there's nothing to delete, and creating or updating anything requires a write-scoped connection (OAuth session or API key). Mechanically, the server exposes three discovery tools — `list_endpoints`, `get_endpoint_schema`, and `call_endpoint` — over the partner API's operations, so your client browses and calls them in natural language rather than needing one hard-coded tool per endpoint. Use the URL for the region your company's portal runs in: | Region | MCP URL | | -------------- | ------------------------------------------ | | Europe (EU) | `https://partner-mcp.journeybee.io/mcp` | | Australia (AU) | `https://au.partner-mcp.journeybee.io/mcp` | Your portal's **API & MCP** page also shows your URL directly. ## Connect with OAuth For interactive clients (Claude, ChatGPT, Cursor, or any OAuth-capable MCP client): 1. In your client, add a remote MCP server or custom connector using your region's URL from the table above. 2. The client opens your Partner Portal sign-in page. Log in as usual. 3. Review and approve access on the consent screen. No API key is needed. 4. You're returned to your client, connected. Tokens refresh automatically; revoke access any time by removing the connector. ## Connect with an API key Headless runtimes, open-source models, and CI can't do the browser sign-in, so they connect with a partner API key instead — the same keys the [Partner API](/guides/partner-api) accepts. Create one on the portal's **API & MCP** page, then send it as a Bearer credential: ```json theme={null} { "mcpServers": { "partner": { "url": "https://partner-mcp.journeybee.io/mcp", "headers": { "Authorization": "Bearer YOUR_API_KEY" } } } } ``` ## Field requirements Leads and deals can carry required or rule-controlled custom fields. The MCP server exposes the same `getPartnerLeadRequirements` / `getPartnerDealRequirements` operations as the [Partner API](/guides/partner-api#field-requirements) through its normal discovery tools — no special MCP handling needed. The recommended call order for a client creating or updating a lead or deal is: call the matching requirements operation first to learn which fields are required or rule-controlled, then create/update, and if that returns a `validation_error`, read `error.details[]` (each entry has a field id and a machine-readable `reason`) to repair and retry. ## Scopes and permissions Your connection carries exactly the permissions of the OAuth session or API key you connected with — `read` and/or `write`. A read-only key or session never sees write tools. ## Troubleshooting **No write tools show up in your client.** Your key or OAuth session only has read access. Create or connect with a key/session that has write access if you need to create or update records. **403 error: "Partner API access is not enabled for this company".** The company that runs your portal hasn't turned on the Partner API module for their account. Contact them to enable it. **403 error: "The Leads/Deals area is not available in your partner portal".** The company hasn't exposed that area to this partnership in the partner portal. Ask the vendor to enable the relevant area; see [Visibility](/guides/partner-api#visibility) in the Partner API guide. Deal creation can also be disabled separately; ask the vendor to enable deal creation if you receive that 403 response. **The connection works, but a record you expect isn't there.** Partner API responses follow the same visibility rules as the partner portal — see [Visibility](/guides/partner-api#visibility) in the Partner API guide. ## Learn more See the [Partner API](/guides/partner-api) guide for authentication details, company and partnership scoping, and visibility rules — the MCP server follows the same rules. # TypeScript SDK Source: https://docs.journeybee.io/guides/sdk Install and use the Journeybee TypeScript SDK # TypeScript SDK The official TypeScript SDK provides typed functions for every API endpoint with full autocomplete and compile-time type checking. ## Installation Published on npm — [**`@journeybee/sdk`**](https://www.npmjs.com/package/@journeybee/sdk): ```bash theme={null} npm install @journeybee/sdk ``` ## Quick start ```typescript theme={null} import { client, listPartners, createLead } from "@journeybee/sdk"; // Configure once — all subsequent calls use this config client.setConfig({ baseUrl: "https://api.journeybee.io/v1", auth: "YOUR_API_KEY", }); // List partners with search const { data: partners } = await listPartners({ query: { search: "acme", per_page: 10 }, }); // Create a lead const { data: lead } = await createLead({ body: { first_name: "Jane", last_name: "Smith", email: "jane@example.com", partnership_uuid: "partner-uuid-here", }, }); ``` ## Authentication Pass your API key to `client.setConfig()`. The SDK sends it as a `Bearer` token on every request automatically. ```typescript theme={null} import { client } from "@journeybee/sdk"; client.setConfig({ baseUrl: "https://api.journeybee.io/v1", auth: "jb_live_your_api_key", }); ``` ## Request patterns Every SDK function accepts an options object with typed `path`, `query`, and `body` fields depending on the endpoint. ### List with pagination and filters ```typescript theme={null} import { listLeads } from "@journeybee/sdk"; const { data } = await listLeads({ query: { page: 1, per_page: 50, status: "new", search: "acme", }, }); // data.data — array of leads // data.pagination — { page, per_page, total, total_pages } ``` ### Get by UUID ```typescript theme={null} import { getPartner } from "@journeybee/sdk"; const { data: partner } = await getPartner({ path: { uuid: "partner-uuid" }, }); ``` ### Create ```typescript theme={null} import { createDeal } from "@journeybee/sdk"; const { data: deal } = await createDeal({ body: { lead_uuid: "lead-uuid", value: 50000, currency_uuid: "currency-uuid", }, }); ``` ### Update ```typescript theme={null} import { updateLead } from "@journeybee/sdk"; const { data: lead } = await updateLead({ path: { uuid: "lead-uuid" }, body: { status: "converted" }, }); ``` ### Delete ```typescript theme={null} import { deleteLead } from "@journeybee/sdk"; await deleteLead({ path: { uuid: "lead-uuid" }, }); ``` ### Sub-resources ```typescript theme={null} import { addLeadTag, removeLeadTag, listLeadNotes } from "@journeybee/sdk"; // Add a tag await addLeadTag({ path: { uuid: "lead-uuid" }, body: { tag_uuid: "tag-uuid" }, }); // Remove a tag await removeLeadTag({ path: { uuid: "lead-uuid", subUuid: "tag-uuid" }, }); // List notes const { data: notes } = await listLeadNotes({ path: { uuid: "lead-uuid" }, }); ``` ## Error handling SDK functions return `{ data, error, response }`. Check `error` for failures: ```typescript theme={null} import { getLead } from "@journeybee/sdk"; const { data, error } = await getLead({ path: { uuid: "nonexistent" }, }); if (error) { console.error("API error:", error); // error.code — "not_found", "unauthorized", etc. // error.message — human-readable description } else { console.log("Lead:", data); } ``` ## Available functions The SDK exports one function per API endpoint. Function names match the `operationId` in the [API Reference](../apis/openapi.json): | Domain | Functions | | ------------------ | ---------------------------------------------------------------------------------------------------- | | **Partners** | `listPartners`, `getPartner`, `createPartner`, `updatePartner` | | **Leads** | `listLeads`, `getLead`, `createLead`, `updateLead`, `deleteLead` | | **Deals** | `listDeals`, `getDeal`, `createDeal`, `updateDeal`, `deleteDeal` | | **Tasks** | `listTasks`, `getTask`, `createTask`, `updateTask`, `deleteTask` | | **Products** | `listProducts`, `getProduct`, `createProduct`, `updateProduct`, `deleteProduct` | | **Contacts** | `listContacts`, `getContact` | | **Commissions** | `listCommissions`, `getCommission`, `createCommission`, `updateCommission`, `deleteCommission` | | **Resources** | `listResources`, `getResource`, `createResource`, `updateResource`, `deleteResource` | | **Certifications** | `listCertifications`, `getCertification`, `createCertification`, `enrollInCertification` | | **Rooms** | `listRooms`, `getRoom`, `createRoom`, `updateRoom`, `deleteRoom` | | **Folders** | `listFolders`, `getFolder`, `createFolder`, `updateFolder`, `deleteFolder` | | **Configuration** | `listPartnerStages`, `listDealStages`, `listTiers`, `listCategories`, `listTags`, `listCustomFields` | Plus sub-resource functions like `addLeadTag`, `listLeadNotes`, `createPartnerContact`, `getDealCommission`, `addResourceBlock`, `addRoomBlock`, `addRoomTier`, and more. See the [API Reference](../apis/openapi.json) for the complete list. # Webhooks Source: https://docs.journeybee.io/guides/webhooks Subscribe to real-time events from your Journeybee workspace # Webhooks Webhooks deliver real-time notifications when data changes in your Journeybee workspace. Use them to sync leads into your CRM, trigger a Zap, notify an ops channel, or drive any downstream workflow. Webhooks today run through the **connector subscription API** (below). A more developer-oriented flow — including raw-signature HMAC, retries, and a UI for managing endpoints — is planned for a later release. ## Quick start Subscribe your endpoint to an event: ```bash theme={null} curl -X POST https://api.journeybee.io/v1/webhooks/subscriptions \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "integration_id": 42, "event": "leads.create", "url": "https://hooks.example.com/journeybee" }' ``` Response: ```json theme={null} { "id": "42.leads.create.1713300000000", "integration_id": 42, "event": "leads.create", "url": "https://hooks.example.com/journeybee", "active": true, "message": "Webhook subscription created successfully" } ``` Your endpoint will now receive a `POST` for every matching event. ## Supported events | Event | Description | | ------------------------- | ----------------------------- | | `leads.create` | A lead was created | | `leads.update` | A lead was updated | | `leads.delete` | A lead was deleted | | `leads.notes_create` | A note was added to a lead | | `leads.notes_update` | A lead note was updated | | `leads.notes_delete` | A lead note was deleted | | `deals.create` | A deal was created | | `deals.update` | A deal was updated | | `deals.delete` | A deal was deleted | | `partners.create` | A partner was added | | `partners.update` | A partner was updated | | `partners.contact_create` | A partner contact was created | | `partners.contact_update` | A partner contact was updated | | `payments.processing` | A payout was initiated | | `payments.completed` | A payout completed | | `payments.failed` | A payout failed | | `mdf.payout_completed` | An MDF claim payout completed | | `mdf.payout_failed` | An MDF claim payout failed | ## Subscription API ### Create a subscription ``` POST /v1/webhooks/subscriptions ``` Body: | Field | Type | Required | Description | | ---------------- | ------- | -------- | --------------------------------------------- | | `integration_id` | integer | yes | The integration this subscription attaches to | | `event` | string | yes | One of the supported events above | | `url` | string | yes | The HTTPS endpoint to POST events to | | `description` | string | no | Free-text note for your own records | The `integration_id` identifies which integration row the subscription hangs off. For Zapier-style self-serve subscriptions, use the public Journeybee connector integration ID. Ask [support](https://support.journeybee.io) if you're unsure which integration to use. ### Delete a subscription ``` POST /v1/webhooks/subscriptions ``` Send `DELETE` with the `hookUrl` body field set to the subscription `id` returned at create time: ```bash theme={null} curl -X DELETE https://api.journeybee.io/v1/webhooks/subscriptions \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "hookUrl": "42.leads.create.1713300000000" }' ``` ## Event delivery Every subscribed event generates a single `POST` to your URL with: * **`Content-Type: application/json`** — the request body is a JSON payload described below. * **`Authorization: Bearer `** — a JWT signed with your integration's UUID as the secret. Verify this before processing the payload. ### Example payload — `leads.create` ```json theme={null} { "lead": { "uuid": "b1a9d5c3-...", "created_at": "2026-04-16T09:12:00.000Z", "updated_at": "2026-04-16T09:12:00.000Z", "status": "new", "source": "form", "company_name": "Acme Corp", "email": "buyer@acme.test", "phone_number": "+1-555-0100", "first_name": "Jane", "last_name": "Buyer", "partnership_name": "Northwind Referrals", "partnership_type": "referral", "created_by_user_uuid": "8e7d...", "created_by_user_email": "rep@northwind.test", "created_by_user_first_name": "Riley", "created_by_user_last_name": "Rep", "custom_fields": [ { "uuid": "cf-...", "label": "Region", "type": "select", "options": [ { "id": 1, "label": "EMEA" }, { "id": 2, "label": "AMER" } ], "value": "EMEA", "custom_field_value_uuid": "cfv-..." } ], "tags": [{ "uuid": "tag-...", "label": "Hot" }], "assigned_users": [ { "uuid": "u-...", "email": "ae@acme.test", "first_name": "Alex", "last_name": "AE" } ], "assigned_contacts": [] }, "configuration": [] } ``` Other events follow the same pattern — the entity key matches the resource (`deal`, `partner`, `contact`, etc.). ## Verifying requests The `Authorization` header contains a JWT signed with HS256 using your **integration UUID** as the shared secret. Decode and verify it to confirm the request is genuine. ### Node.js (jsonwebtoken) ```javascript theme={null} import jwt from "jsonwebtoken"; const INTEGRATION_UUID = process.env.JOURNEYBEE_INTEGRATION_UUID; app.post("/webhook", (req, res) => { const header = req.headers.authorization ?? ""; const token = header.startsWith("Bearer ") ? header.slice(7) : null; if (!token) return res.status(401).end(); try { const claims = jwt.verify(token, INTEGRATION_UUID, { algorithms: ["HS256"], }); // claims = { company_uuid, user_uuid, event_id, external_settings, api_key } processEvent(claims.event_id, req.body); res.status(200).end(); } catch { res.status(401).end(); } }); ``` ### Token claims | Claim | Description | | ------------------- | --------------------------------------------------------------- | | `company_uuid` | The Journeybee company the event belongs to | | `user_uuid` | The user that triggered the event (falls back to an admin user) | | `event_id` | The event name (e.g. `lead_created`, `deal_updated`) | | `api_key` | Optional API key for callbacks into Journeybee | | `external_settings` | Your stored integration settings, including `authorisation[]` | Always verify the JWT before acting on a payload. The integration UUID is the shared secret — do not commit it to source control and do not expose it in client-side code. ## Delivery semantics * **At-most-once**: the worker sends one POST per event. There are no automatic retries on 5xx responses today, so your endpoint must return 2xx quickly and queue work asynchronously. * **Timeouts**: slow endpoints (>5s) may be dropped. Return a 2xx as soon as you've persisted the event, then process it out of band. * **Ordering**: events are not guaranteed to arrive in order. Use the `updated_at` field on the payload to reconcile. * **Duplicates**: duplicates are rare but possible. Key your idempotency off the entity `uuid` plus `event_id`. Retries, signed timestamps, and a delivery log are on the roadmap (Platform phase 15). If you need guaranteed delivery today, poll the relevant list endpoints on an interval and reconcile against your local state. ## Troubleshooting * **Receiving 401s?** Double-check that you're verifying with the integration UUID (not your API key) and using HS256. * **Not receiving events?** Confirm the subscription exists by listing your current subscriptions: ```bash theme={null} curl https://api.journeybee.io/v1/webhooks/subscriptions \ -H "Authorization: Bearer YOUR_API_KEY" ``` * **Want to test delivery?** Use a tool like [webhook.site](https://webhook.site) as the `url` and trigger an event (create a test lead, update a partner) from the app. ## Related * [Authentication](/guides/authentication) — API key setup * [Errors](/guides/errors) — error response shapes and codes # Journeybee API Source: https://docs.journeybee.io/index Build integrations with the Journeybee PRM platform # Journeybee API Build integrations with the Journeybee Partner Relationship Management platform. Manage partners, contacts, deals, and more programmatically. ## Quick start 1. Get your API key from **Settings > API Keys** in the [Journeybee dashboard](https://app.journeybee.io) 2. Install the [`@journeybee/sdk`](https://www.npmjs.com/package/@journeybee/sdk) package and make your first request: ```bash theme={null} npm install @journeybee/sdk ``` ```typescript theme={null} import { client, listPartners } from "@journeybee/sdk"; client.setConfig({ baseUrl: "https://api.journeybee.io/v1", auth: "YOUR_API_KEY", }); const { data } = await listPartners(); ``` Or use the API directly: ```bash theme={null} curl https://api.journeybee.io/v1/partners \ -H "Authorization: Bearer YOUR_API_KEY" ``` 3. Explore the [API Reference](apis/openapi.json) for all available endpoints ## What you can do * **Manage partners** — Create, update, and query partnerships with stages, tiers, categories, and tags * **Track leads and deals** — Full pipeline management with commissions and payments * **Track contacts** — Add and manage contacts for each partner * **Build learning content** — Create resources with content blocks, certifications with curricula, and track enrollments * **Build partner portal rooms** — Create custom portal pages with text, video, action bars, goals, and more * **Organize with custom fields** — Define custom fields and set values per entity * **Manage tasks** — Create, assign, and track tasks across leads and deals * **Configure settings** — Manage pipeline stages, tiers, categories, tags, and custom field definitions * **Connect AI assistants** — Use the [MCP server](guides/mcp.md) to let Claude manage your data ## Base URL ``` https://api.journeybee.io/v1 ``` ## Next steps * [Authentication](guides/authentication.md) — How to authenticate requests * [TypeScript SDK](guides/sdk.md) — Install and use the typed SDK * [MCP Server](guides/mcp.md) — Connect AI assistants to Journeybee * [Embedding lead forms](guides/embedding-forms.md) — Drop a lead-capture form on any site with one script tag * [Embedding the partner portal](guides/embedding-portal.md) — Embed the authenticated partner portal in your app * [Pagination](guides/pagination.md) — Navigating paginated results * [Error handling](guides/errors.md) — Understanding error responses * [API Reference](apis/openapi.json) — Full endpoint documentation # Create a partnership deal Source: https://docs.journeybee.io/partner-api-reference/partner/create-a-partnership-deal /partner-openapi.json post /partner/{partnershipId}/deals Custom fields may be required or rule-controlled — call getPartnerDealRequirements first to learn the field/rule constraints. Validation failures return per-field `details` with machine-readable `reason` codes. # Create a partnership lead Source: https://docs.journeybee.io/partner-api-reference/partner/create-a-partnership-lead /partner-openapi.json post /partner/{partnershipId}/leads Custom fields may be required or rule-controlled — call getPartnerLeadRequirements first to learn the field/rule constraints. Validation failures return per-field `details` with machine-readable `reason` codes. # Get a partnership deal Source: https://docs.journeybee.io/partner-api-reference/partner/get-a-partnership-deal /partner-openapi.json get /partner/{partnershipId}/deals/{dealId} # Get a partnership lead Source: https://docs.journeybee.io/partner-api-reference/partner/get-a-partnership-lead /partner-openapi.json get /partner/{partnershipId}/leads/{leadId} # Get deal custom field requirements Source: https://docs.journeybee.io/partner-api-reference/partner/get-deal-custom-field-requirements /partner-openapi.json get /partner/{partnershipId}/deals/requirements Returns the custom fields and rules that createPartnerDeal and updatePartnerDeal enforce for this partnership — the fields (with their required flag) and active rules (visibility, locking, auto-population) a deal submission must satisfy. 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. # Get lead custom field requirements Source: https://docs.journeybee.io/partner-api-reference/partner/get-lead-custom-field-requirements /partner-openapi.json get /partner/{partnershipId}/leads/requirements Returns the custom fields and rules that createPartnerLead and updatePartnerLead enforce for this partnership — the fields (with their required flag) and active rules (visibility, locking, auto-population) a lead submission must satisfy. # Get partner identity and accessible partnerships Source: https://docs.journeybee.io/partner-api-reference/partner/get-partner-identity-and-accessible-partnerships /partner-openapi.json get /partner/me # List partnership deals Source: https://docs.journeybee.io/partner-api-reference/partner/list-partnership-deals /partner-openapi.json get /partner/{partnershipId}/deals # List partnership leads Source: https://docs.journeybee.io/partner-api-reference/partner/list-partnership-leads /partner-openapi.json get /partner/{partnershipId}/leads # Update a partnership deal Source: https://docs.journeybee.io/partner-api-reference/partner/update-a-partnership-deal /partner-openapi.json patch /partner/{partnershipId}/deals/{dealId} Custom fields may be required or rule-controlled — call getPartnerDealRequirements first to learn the field/rule constraints. Validation failures return per-field `details` with machine-readable `reason` codes. # Update a partnership lead Source: https://docs.journeybee.io/partner-api-reference/partner/update-a-partnership-lead /partner-openapi.json patch /partner/{partnershipId}/leads/{leadId} Custom fields may be required or rule-controlled — call getPartnerLeadRequirements first to learn the field/rule constraints. Validation failures return per-field `details` with machine-readable `reason` codes.