LedgerBee Developer
  • Getting started
  • Conventions
  • Products
  • API Reference
Information
Accounts
    List accountsget
API Key
    Get current API keyget
Company
    Get company detailsget
Customers
    List customersgetCreate a customerpostGet a single customergetUpdate a customerpatchList a customer's contactsgetList a customer's departmentsgetList customer groupsget
Dimensions
    List all dimensionsgetCreate a new dimension categorypostGet a single dimension by IDgetUpdate a dimension categoryputDelete a dimension categorydeleteCreate a new dimension valuepostUpdate a dimension valueputDelete a dimension valuedelete
Document Templates
    List document templatesget
Journal Categories
    List journal categoriesget
Journal Entries
    Create multiple journal entries in batch (DEPRECATED — migrate to v2)postList journal entriesgetCreate a single journal entry (DEPRECATED — use POST /api/v2/journal-entries)postGet a single journal entrygetUpdate a journal entry — correct the reference and/or set your external system idpatchGet attachments for a journal entrygetReverse posted journal entries in batchpostReverse a posted journal entrypost
MCP
    LedgerBee Developer MCP Serverpost
Meters
    List meter subscriptions by IDgetReport usagepostList meter subscriptions by nameget
Product Groups
    List product groupsgetCreate a product grouppostGet a product groupgetDelete a product groupdeleteUpdate a product grouppatch
Product Prices
    Create a product pricepostGet a product pricegetDelete a product pricedeleteUpdate a product pricepatchActivate a product pricepostArchive a product pricepostDisable a product pricepostSet a price as the product defaultpost
Products
    List productsgetCreate a productpostGet a productgetDelete a productdeleteUpdate a productpatch
Projects
    List all projectsgetIdempotent project upsertputCreate a projectpostGet a projectgetUpdate a projectputGet project profitgetAdd a subprojectpostUpdate a subprojectputDelete a subprojectdelete
Subscriptions
    List customer subscriptionsgetAssign a subscription to a customerpostGet a customer subscription by its stable idgetUpdate a customer subscriptionpatchList a subscription’s pending scheduled changesgetCancel a single pending scheduled changedeleteCancel a customer subscriptionpostList subscription plansgetArchive a subscription planpost
VAT Codes
    List VAT codesget
Webhook Endpoints
    List webhook endpointsgetCreate a webhook endpointpostRetrieve a webhook endpointgetUpdate a webhook endpointputDelete a webhook endpointdeleteRoll the signing secretpostSend a test eventpost
Portal SSO
    Published RP signing keys (JWKS) for a tenant's private_key_jwt SSOgetMint a customer-portal SSO handoff referencepostProvision a customer + grant portal access (partner JIT provisioning)postRevoke (force-refresh) a user's customer-portal sessionspost
Credit Notes
    List credit notesgetCreate a credit notepostGet a credit note by IDgetGet the delivery status of a credit notegetDownload the credit note PDFgetSend a credit note to the customerpost
Payment Methods
    List saved payment methods for a customergetApply a payment method to all active subscriptions for a customerpostCancel a saved card on the payment providerdeleteUnassign a payment method from a customerdeleteGenerate a link for the customer to save a card for future paymentspostSet a saved payment method as the customer defaultput
Draft Journal Entries
    Create multiple draft journal entries in batchpostList draft journal entriesgetCreate a draft journal entrypostGet a single draft journal entrygetDelete a draft journal entrydeleteBook a draft journal entrypost
Invoices
    List invoicesgetCreate a draft invoicepostGet an invoice by IDgetGet the delivery status of an invoicegetDownload the invoice PDFgetSend an invoice to the customerpost
Order Confirmations
    List order confirmationsgetCreate a draft order confirmationpostGet an order confirmation by idgetUpdate a draft order confirmationpatchConvert an order confirmation into an invoicepostDownload an order confirmation as PDFgetSend an order confirmationpost
Portal Plans
    List the full plan cataloguegetGet one plan by idgetResolve the gated pricing catalogue for an identified buyer (partner display)post
Quotes
    List quotesgetCreate a draft quotepostGet a quote by idgetUpdate a draft quotepatchConvert a quotepostDownload a quote as PDFgetSend a quotepost
Vendors
    List vendorsgetCreate a vendorpostList vendor contactsgetList vendor departmentsgetList vendor groupsget
Draft Journal Entries (v2)
    Create draft journal entries in batch (v2)postList draft journal entries (v2)getCreate a draft journal entry (v2)postGet a single draft journal entry (v2)getDelete a draft journal entry (v2)deleteUpdate a draft journal entry (v2)patchBook a draft journal entry (v2)post
Journal Entries (v2)
    Create a journal entry (v2)postCreate journal entries in batch (v2)post
Schemas
LedgerBee Public API
LedgerBee Public API

Portal SSO

Download schema

Published RP signing keys (JWKS) for a tenant's private_key_jwt SSO

GET
https://api.ledgerbee.com/api
/.well-known/portal-sso/{slug}/jwks.json

The jwks_uri a partner registers at their OpenID Provider so it can verify our private_key_jwt client assertions. Returns an RFC 7517 JWK Set of the tenant's current signing key plus — during a rotation grace window — the immediately-previous key, each keyed by kid. Public key material only; no authentication required. An unknown slug or a tenant with no signing key returns an empty key set (never a 404). Cached for 5 minutes; OPs re-fetch on an unknown kid.

Published RP signing keys (JWKS) for a tenant's private_key_jwt SSO › path Parameters

slug
​string · required

The tenant's public portal slug.

Example: acme

Published RP signing keys (JWKS) for a tenant's private_key_jwt SSO › Responses

200
PublicPortalSsoJwks
​object[] · required

The published public JWK(s). Public key material only — never any private component.

Example: [{"kty":"RSA","use":"sig","alg":"RS256","kid":"abc123","n":"…","e":"AQAB"}]
GET/.well-known/portal-sso/{slug}/jwks.json
curl https://api.ledgerbee.com/api/.well-known/portal-sso/:slug/jwks.json
Example Responses
{ "keys": [ { "kty": "RSA", "use": "sig", "alg": "RS256", "kid": "abc123", "n": "…", "e": "AQAB" } ] }
json
application/json

Mint a customer-portal SSO handoff reference

POST
https://api.ledgerbee.com/api
/v1/portal-sso/handoff/mint
x-api-key (header)
or
OAuth 2.0

Back-channel single sign-on into the LedgerBee customer portal. A partner's backend calls this to mint an opaque, single-use, 60-second reference for a customer-portal user; the user's browser then redeems that reference at the portal-API host (.portal-api.ledgerbee.com/api/auth/sso/handoff/redeem?ref=) to establish a portal session — NOT the portal SPA host (.portal.ledgerbee.com), which serves the static app and silently ignores the ref. The reference carries no identity — it is a server-resolved pointer. The tenant is resolved from the API key, never the request body, and every asserted organization is validated against that tenant. At least one membership must be asserted: the portal user, organization, and membership are all created at redeem from the asserted customers (no prior provision call is required), and a handoff with no membership cannot establish a session and is rejected with a 400. Requires the portal-sso-mint scope.

Mint a customer-portal SSO handoff reference › Request Body

PublicHandoffMintRequest
email
​string · required

Email of the partner-provisioned user. Links a first-time SSO login to an existing invited customer-portal user.

Example: jane.doe@partner-customer.com
sub
​string · required

The partner's stable, unique identifier for the user. Keyed together with the per-tenant handoff issuer to resolve the portal user across logins.

Example: partner-user-7f3a9c21
​PublicHandoffMembership[] · required

At least one customer membership to grant this user — required. The customer's portal organization (an opaque internal container) and the membership are created automatically server-side at redeem, so a prior provision call is NOT needed; you only ever reference customers, never organizations. Validated at mint: a customerId that isn't a customer of the API key's tenant, or an unknown role, is rejected with a 400 (rather than failing the user's browser redirect). A handoff with no membership can't establish a portal session, so an empty/omitted array is rejected here rather than minting a ref that would fail at redeem.

Example: [{"customerId":"550e8400-e29b-41d4-a716-446655440010","role":"OWNER"}]
name
​string

Display name of the user; used to populate the customer-portal user's name on first login.

Example: Jane Doe

Mint a customer-portal SSO handoff reference › Responses

PublicHandoffMintResponse
ref
​string · required

Opaque, single-use, short-TTL reference. Carries NO identity — the identity lives only in server-side storage keyed by this reference until it is redeemed once at the customer portal.

Example: Yk3pQ9sV2nR7tW1xZ4cB8mL6dF0aH5jE2gN9uK3pQ7s
expiresAt
​string · required

ISO-8601 timestamp after which the reference can no longer be redeemed (60 seconds after minting).

Example: 2026-06-03T12:34:56.000Z
POST/v1/portal-sso/handoff/mint
curl https://api.ledgerbee.com/api/v1/portal-sso/handoff/mint \ --request POST \ --header 'Content-Type: application/json' \ --header 'x-api-key: <api-key>' \ --data '{ "email": "jane.doe@partner-customer.com", "sub": "partner-user-7f3a9c21", "name": "Jane Doe", "memberships": [ { "customerId": "550e8400-e29b-41d4-a716-446655440010", "role": "OWNER" } ] }'
Example Request Body
{ "email": "jane.doe@partner-customer.com", "sub": "partner-user-7f3a9c21", "name": "Jane Doe", "memberships": [ { "customerId": "550e8400-e29b-41d4-a716-446655440010", "role": "OWNER" } ] }
json
Example Responses
{ "ref": "Yk3pQ9sV2nR7tW1xZ4cB8mL6dF0aH5jE2gN9uK3pQ7s", "expiresAt": "2026-06-03T12:34:56.000Z" }
json
application/json

Provision a customer + grant portal access (partner JIT provisioning)

POST
https://api.ledgerbee.com/api
/v1/portal-sso/provision
x-api-key (header)
or
OAuth 2.0

Ensures a customer (referenced by customerId, or upserted idempotently by customer.customerNumber within the API key's tenant), bootstraps its portal organization, and grants a credential-less portal user an active membership — so a net-new end-customer can sign in via SSO/handoff. No invitation email is sent. Set mintHandoff=true (and carry the portal-sso-mint scope) to also receive a one-time handoff reference in the response for a single round-trip 'Go to portal'. Set mintCheckoutBindToken=true (also requires the portal-sso-mint scope) to receive a one-time checkout-bind reference the partner embeds so the embedded checkout subscribes directly against this customer. Requires the portal-provision scope.

Provision a customer + grant portal access (partner JIT provisioning) › Request Body

PublicProvisionCustomerRequest
email
​string · required

Email of the portal user to grant access to. A credential-less portal user is created if none exists.

Example: jane.doe@acme.example
customerId
​string

The customer's LedgerBee id. The customer must already exist in the API key's tenant; this endpoint never creates one from an id (a 404 CUSTOMER_NOT_FOUND is returned if it doesn't). Supply either customerId or a customer payload.

Example: 550e8400-e29b-41d4-a716-446655440010
​object

A customer to upsert, identified idempotently by its customerNumber: an existing customer of the API key's tenant with that number is reused, otherwise a new one is created with this payload. Same shape as the gated POST /customers request (so vatZone and countryCode are required here). If customer.email is omitted, the portal-user email below is used as the customer's contact email (and its auto-created default contact). Supply either this or customerId.

role
​string · enum

Membership role to grant. Defaults to USER; the org's first member is force-assigned OWNER regardless.

Enum values:
OWNER
ADMIN
BILLING_ADMIN
USER
Example: USER
userName
​string

Display name of the portal user (set on first creation).

Example: Jane Doe
organizationDisplayName
​string

Display name for the customer organization (first-invite bootstrap).

mintHandoff
​boolean

When true, also mint a back-channel handoff reference in the same call (one round-trip "Go to portal"). Requires the API key to ALSO carry the portal-sso-mint scope, and the tenant to have back-channel handoff enabled.

Example: false
sub
​string

The partner's stable, unique user identifier — required when mintHandoff is true; keys the handoff identity (with the per-tenant handoff issuer).

Example: partner-user-7f3a9c21
mintCheckoutBindToken
​boolean

When true, also mint a one-time CHECKOUT-BIND reference in the same call — an opaque token the partner's fetchBindToken provider returns to the embed at checkout-start so the embedded checkout subscribes directly against THIS customer instead of staging an anonymous magic-link signup. Requires the API key to ALSO carry the portal-sso-mint scope. Independent of mintHandoff (login): a checkout-bind ref can never mint a login session.

Example: false
clientReferenceId
​string

Your own opaque reference for this order (Stripe client_reference_id parity), e.g. your internal order or cart id. It rides the minted checkout-bind ref onto the resulting subscription (as partnerReferenceId) and into every later subscription webhook, so you can reconcile our subscription to your record. Server-to-server only — it never reaches a client/iframe surface. Max 200 chars, [A-Za-z0-9_-] only.

Example: order_7f3a9c21

Provision a customer + grant portal access (partner JIT provisioning) › Responses

PublicProvisionCustomerResponse
customerId
​string · required

Resolved customer id.

Example: 550e8400-e29b-41d4-a716-446655440010
customerCreated
​boolean · required

Whether a new customer was created (false = an existing one was reused).

Example: true
portalUserId
​string · required

Resolved portal user id.

Example: 550e8400-e29b-41d4-a716-446655440020
membershipId
​string · required

The active organization membership id.

Example: 550e8400-e29b-41d4-a716-446655440030
role
​string · enum · required

The granted membership role.

Enum values:
OWNER
ADMIN
BILLING_ADMIN
USER
Example: OWNER
handoffRef
​string | null

Opaque, single-use, 60s handoff reference — present only when mintHandoff was requested. Null otherwise.

Example: Yk3pQ9sV2nR7tW1xZ4cB8mL6dF0aH5jE2gN9uK3pQ7s
handoffExpiresAt
​string | null

ISO-8601 expiry of the handoff reference, when minted. Null otherwise.

Example: 2026-06-04T12:34:56.000Z
checkoutBindToken
​string | null

Opaque, single-use, 60s checkout-bind reference — present only when mintCheckoutBindToken was requested. Null otherwise. The partner's fetchBindToken provider returns this to the embed at checkout-start; the embedded checkout redeems it to subscribe directly against this customer.

Example: Yk3pQ9sV2nR7tW1xZ4cB8mL6dF0aH5jE2gN9uK3pQ7s
checkoutBindExpiresAt
​string | null

ISO-8601 expiry of the checkout-bind reference, when minted. Null otherwise.

Example: 2026-06-04T12:34:56.000Z
POST/v1/portal-sso/provision
curl https://api.ledgerbee.com/api/v1/portal-sso/provision \ --request POST \ --header 'Content-Type: application/json' \ --header 'x-api-key: <api-key>' \ --data '{ "customerId": "550e8400-e29b-41d4-a716-446655440010", "customer": { "customerType": "BUSINESS", "customerNumber": "C001", "name": "Acme Corporation", "vatZone": "domestic", "customerGroupId": "01HQ3Y1234567890ABCDEFGHIJ", "email": "contact@acme.com", "phone": "+45 12345678", "address": "123 Main Street", "city": "Copenhagen", "postalCode": "2100", "countryCode": "DK", "vatNumber": "DK12345678", "eanNumber": "5790000000001", "website": "https://acme.com", "notes": "Key account customer", "locale": "da-DK", "language": "da", "creditLimit": 50000, "openingBalance": 1000, "paymentTerms": 30, "paymentTermType": "NET_DAYS", "defaultCashAccountId": "defaultCashAccountId", "defaultCurrency": "DKK", "poNumber": "PO-12345", "ourReference": "Jane Doe", "invoiceDeliveryTypePreference": "Email", "reminderConfigId": "reminderConfigId", "isActive": true, "defaultContact": { "name": "John Doe", "email": "john@acme.com", "phone": "+45 12345678" }, "invoiceTemplateId": "invoiceTemplateId", "creditNoteTemplateId": "creditNoteTemplateId", "quoteTemplateId": "quoteTemplateId", "orderConfirmationTemplateId": "orderConfirmationTemplateId", "parentInvoiceTemplateId": "parentInvoiceTemplateId", "includeChildDetailsPdf": true, "parentCustomerId": "parentCustomerId", "billingRelationship": "self", "consolidationMode": "single_invoice", "billingDay": 0, "metadata": { "key": "string" } }, "email": "jane.doe@acme.example", "role": "USER", "userName": "Jane Doe", "organizationDisplayName": "organizationDisplayName", "mintHandoff": false, "sub": "partner-user-7f3a9c21", "mintCheckoutBindToken": false, "clientReferenceId": "order_7f3a9c21" }'
Example Request Body
{ "customerId": "550e8400-e29b-41d4-a716-446655440010", "customer": { "customerType": "BUSINESS", "customerNumber": "C001", "name": "Acme Corporation", "vatZone": "domestic", "customerGroupId": "01HQ3Y1234567890ABCDEFGHIJ", "email": "contact@acme.com", "phone": "+45 12345678", "address": "123 Main Street", "city": "Copenhagen", "postalCode": "2100", "countryCode": "DK", "vatNumber": "DK12345678", "eanNumber": "5790000000001", "website": "https://acme.com", "notes": "Key account customer", "locale": "da-DK", "language": "da", "creditLimit": 50000, "openingBalance": 1000, "paymentTerms": 30, "paymentTermType": "NET_DAYS", "defaultCashAccountId": "defaultCashAccountId", "defaultCurrency": "DKK", "poNumber": "PO-12345", "ourReference": "Jane Doe", "invoiceDeliveryTypePreference": "Email", "reminderConfigId": "reminderConfigId", "isActive": true, "defaultContact": { "name": "John Doe", "email": "john@acme.com", "phone": "+45 12345678" }, "invoiceTemplateId": "invoiceTemplateId", "creditNoteTemplateId": "creditNoteTemplateId", "quoteTemplateId": "quoteTemplateId", "orderConfirmationTemplateId": "orderConfirmationTemplateId", "parentInvoiceTemplateId": "parentInvoiceTemplateId", "includeChildDetailsPdf": true, "parentCustomerId": "parentCustomerId", "billingRelationship": "self", "consolidationMode": "single_invoice", "billingDay": 0, "metadata": { "key": "string" } }, "email": "jane.doe@acme.example", "role": "USER", "userName": "Jane Doe", "organizationDisplayName": "organizationDisplayName", "mintHandoff": false, "sub": "partner-user-7f3a9c21", "mintCheckoutBindToken": false, "clientReferenceId": "order_7f3a9c21" }
json
Example Responses
{ "customerId": "550e8400-e29b-41d4-a716-446655440010", "customerCreated": true, "portalUserId": "550e8400-e29b-41d4-a716-446655440020", "membershipId": "550e8400-e29b-41d4-a716-446655440030", "role": "OWNER", "handoffRef": "Yk3pQ9sV2nR7tW1xZ4cB8mL6dF0aH5jE2gN9uK3pQ7s", "handoffExpiresAt": "2026-06-04T12:34:56.000Z", "checkoutBindToken": "Yk3pQ9sV2nR7tW1xZ4cB8mL6dF0aH5jE2gN9uK3pQ7s", "checkoutBindExpiresAt": "2026-06-04T12:34:56.000Z" }
json
application/json

Revoke (force-refresh) a user's customer-portal sessions

POST
https://api.ledgerbee.com/api
/v1/portal-sso/sessions/revoke
x-api-key (header)
or
OAuth 2.0

Immediately invalidates every active portal session of the partner-provisioned user (identified by the same sub used at mint), forcing re-authentication on their next request. Use for proactive offboarding — it tightens the residual window from the session TTL to immediate. Deprovisioning at the OP is what then prevents re-login. Requires the portal-sso-mint scope.

Revoke (force-refresh) a user's customer-portal sessions › Request Body

PublicRevokeSessionsRequest
sub
​string · required

The partner's stable subject id for the user — the same sub used when minting their handoff. Every active portal session of that user is force-refreshed.

Example: partner-user-7f3a9c21

Revoke (force-refresh) a user's customer-portal sessions › Responses

PublicRevokeSessionsResponse
revoked
​number · required

Number of memberships whose sessions were force-refreshed (0 when the user has no active sessions).

Example: 2
POST/v1/portal-sso/sessions/revoke
curl https://api.ledgerbee.com/api/v1/portal-sso/sessions/revoke \ --request POST \ --header 'Content-Type: application/json' \ --header 'x-api-key: <api-key>' \ --data '{ "sub": "partner-user-7f3a9c21" }'
Example Request Body
{ "sub": "partner-user-7f3a9c21" }
json
Example Responses
{ "revoked": 2 }
json
application/json

Webhook EndpointsCredit Notes