J.Jupid Docs

HTTP API reference

Authentication, request schemas, responses, and retry rules for Jupid's business API.

The API adds a programmatic source alongside Plaid and CSV. Your backend connects a user and business, receives their Jupid IDs, and sends accounts and transactions. No browser session or user interaction is required.

New to the API? Follow the API quickstart to sign a token, connect a business, and upload an account and transaction.

Use the backend URL and signing configuration Jupid supplies for your environment. The base path is /api/v1. The OpenAPI document is served by the backend; use that same path on your staging backend when testing. Confirm API enablement for the target environment before integration.

Connect a user and business

Sign an HS256 JWT using the Embed claim format. Keep the signing secret on your server. sub identifies the person, and stays the same when that person switches businesses. Send their complete current list of businesses and the selected business ID:

{
  "business_id": "business-123",
  "businesses": [
    { "id": "business-123", "name": "Acme Studio", "role": "owner" },
    { "id": "business-456", "name": "Acme Shop", "role": "member" }
  ],
  "tier": "paid"
}
FieldRequiredDescription
business_idYesSelected external business ID. Must occur in businesses; use null only for an empty list.
businessesYesComplete list of businesses currently available to this person. IDs must be unique. An empty list revokes all their business access for this partner.
businesses[].idYesYour stable business ID, 1–200 characters. Shared by all users of that business, scoped to your partner ID.
businesses[].nameYesBusiness name, 1–120 characters after trimming.
businesses[].roleYesThis person's role: owner or member. Multiple owners are equal; a business can initially have only members.
tierNofree or paid, according to your partner agreement. Applied to each listed business.

Jupid creates missing businesses and reconciles this person's memberships and roles together. Omitting a previously listed business revokes access to it. Other people's access, native Jupid companies, and other partners' memberships are unaffected. Adding a person to an existing business gives them its existing financial history; it does not copy or move records.

Send the actual role at every authorization. Owners can perform owner-only financial operations; members can use shared financial data. Access and role changes for these companies are managed by your backend, including when the person is an owner. There is no viewer role or automatic promotion of the first person to owner.

Send the agreed tier consistently across users of a business. Each connection replaces the source's saved tier; omission does not preserve a previous value. Partner sponsorship applies in the listed company context and does not create a personal Stripe subscription. This payload contains no account snapshot and rejects undocumented fields. Upload financial history through the data routes below.

Send the token in two headers, with no request body:

curl --request POST "$JUPID_BACKEND_URL/api/v1/auth/partner" \
  --header "X-Jupid-Partner: $JUPID_PARTNER_ID" \
  --header "Authorization: Bearer $JUPID_TOKEN"
{
  "user_id": "10000000-0000-4000-8000-000000000001",
  "organization_id": "10000000-0000-4000-8000-000000000002",
  "source_id": "10000000-0000-4000-8000-000000000003",
  "organizations": [
    {
      "business_id": "business-123",
      "organization_id": "10000000-0000-4000-8000-000000000002",
      "source_id": "10000000-0000-4000-8000-000000000003",
      "role": "owner"
    },
    {
      "business_id": "business-456",
      "organization_id": "10000000-0000-4000-8000-000000000004",
      "source_id": "10000000-0000-4000-8000-000000000005",
      "role": "member"
    }
  ]
}

Save these IDs. Repeated calls reuse the same user, organization, and source. Different people with the same partner business ID share its organization and source. Top-level organization_id and source_id refer to the selected business; organizations maps the complete supplied list.

IDMeaningUse it for
user_idThe mapped Jupid person.Your integration's user mapping.
organization_idThe internal Jupid business shared by its members.The business segment of each HTTP URL.
source_idYour API connection within that business.Uploading accounts.

Data requests require both the signed business_id and a current unrevoked membership matching the URL's organization_id. Changing the URL or sending an old role does not grant access. The selected company in the Jupid UI does not retarget HTTP requests. Data requests do not synchronize memberships; call the authorization endpoint when access changes.

Switching and revoking access

To switch, keep sub unchanged, change business_id, and send the complete businesses list again. An embedded login with this token activates that company through the same membership selection used by Jupid. For a partner host switch, destroy and remount the embed with a freshly issued token. Calling the HTTP authorization endpoint alone does not switch the UI.

To remove all access, your backend can authorize this payload without opening an iframe:

{ "business_id": null, "businesses": [] }

The response retains user_id, returns organizations: [], and sets organization_id and source_id to null. Opening an embedded session with this payload applies the revocations and returns HTTP 403.

After reconciliation, subsequent server checks deny removed memberships, including data requests carrying an older token. Remaining members retain the company's financial history. Reloading and reauthorizing the iframe applies a fresh list; reloading alone does not obtain revocations from your system. There is no background Linker permission feed or automatic token refresh loop. Previously displayed or downloaded data cannot be withdrawn by reauthorization.

JWT expiration is checked at authorization and on each HTTP API request. It does not expire an already created Jupid browser session. Tokens remain replayable until exp: an older still-valid token sent to the authorization endpoint can reapply its access list. Jupid applies authorizations in commit order, without ordering them by iat or consuming jti. Issue short-lived fresh tokens and serialize authorization changes for each person.

Existing mappings keep their original Jupid sign-in email. A new partner identity is not attached to another Jupid user merely because their emails match; contact Jupid if connecting that identity fails.

Upload accounts

POST /organizations/{organization_id}/accounts

Send Content-Type: application/json with a source_id and an array of accounts. One account is an array of one item.

{
  "source_id": "10000000-0000-4000-8000-000000000003",
  "accounts": [
    {
      "external_id": "checking-123",
      "name": "Operating Checking",
      "type": "depository",
      "currency": "USD",
      "current_balance": 12000.34
    }
  ]
}

All five account fields are required:

FieldTypeAccepted value
external_idstringYour stable account ID, 1–256 characters.
namestringAccount name, 1–256 characters after trimming whitespace.
typestringdepository, credit, loan, investment, brokerage, or other.
currencystringThree uppercase letters, such as USD.
current_balancenumberBalance in currency units, with at most two decimal places.

Balances and transaction amounts must be between −9,999,999,999,999.99 and 9,999,999,999,999.99. Upload bodies reject fields outside the documented schema.

{
  "accounts": [
    {
      "id": "10000000-0000-4000-8000-000000000004",
      "external_id": "checking-123"
    }
  ]
}

Account uploads target the connected API source returned by the connection endpoint. Use the returned account id for transaction requests.

Upload transactions

POST /organizations/{organization_id}/accounts/{account_id}/transactions

{
  "transactions": [
    {
      "external_id": "bank-tx-456",
      "amount": -42.50,
      "date": "2026-09-08",
      "description": "Office supplies",
      "pending": false,
      "currency": "USD"
    }
  ]
}
FieldRequiredAccepted value
external_idYesYour stable transaction ID, 1–256 characters.
amountYesNumber in currency units, with at most two decimal places. Same range as account balances.
dateYesA real calendar date in YYYY-MM-DD format.
descriptionYesTransaction description, 1–4,096 characters.
pendingNoBoolean; defaults to false.
currencyNoThree uppercase letters; defaults to the account currency.
  • Amounts use Jupid's signs: positive incoming, negative outgoing. Send dollars for USD, not cents. At most two decimal places are accepted. Plaid transaction amounts use the opposite signs and need conversion.
  • date is a real calendar date in YYYY-MM-DD format. Jupid sets both authorized and processed timestamps to midnight UTC on that date.
  • Omitting pending means false. Omitting currency uses the account's currency. Send the complete current transaction when updating it.

The API stores the currency you supply; it does not convert amounts between currencies.

{
  "transactions": [
    {
      "id": "10000000-0000-4000-8000-000000000005",
      "external_id": "bank-tx-456"
    }
  ]
}

The account must belong to a connected API source in this business.

Repeats, updates, and initial history

Keep each account and transaction external_id stable. IDs are unique within the source and business; transaction IDs must be unique across all accounts in that source. An existing transaction cannot move to another account. Idempotency comes from keeping the same external_id for each record.

Sending an existing ID updates its bank fields and preserves its Jupid ID. Response mappings are keyed by external_id; do not rely on response order. Missing records in a request are left unchanged. Duplicate IDs within one batch are rejected.

Each upload accepts 1–500 records, with at most 1 MiB per HTTP request. For example, 50,000 transactions fit into 100 requests of 500 records if each request meets the byte limit. Send changes for the same account sequentially; different businesses can upload independently.

A successful transaction response means the batch was saved and its background-processing event was accepted. Categorization runs through normal Jupid processing after the response. The response does not wait for AI work. Paid-tier processing follows the existing partner agreement; unpaid processing keeps Jupid's normal limit.

Retry the same batch with backoff after a network failure or HTTP 500, including when you did not receive a response. A retry also resends the processing event. Reduce the batch on HTTP 413; correct invalid fields on 422.

Upserts preserve filing state, comments, context, categories, counterparties, and the transaction's deletion flag. These fields cannot be supplied in an upload. Resending a deleted transaction does not restore it.

Delete records

RequestResult
DELETE /organizations/{organization_id}/accounts/{account_id}Disconnect the account and retain transaction history. Requires the business owner. The source is revoked if no active accounts remain.
DELETE /organizations/{organization_id}/transactions/{transaction_id}Soft-delete the transaction.

Both return HTTP 200. For example, deleting a transaction returns:

{
  "transaction_id": "10000000-0000-4000-8000-000000000005",
  "deleted": true
}

Account deletion returns account_id instead. Repeating a deletion succeeds. After disconnecting the last account, call the connection endpoint again to reconnect its API source before uploading accounts. Uploading an account again reactivates it; resending a deleted transaction does not restore it.

The delete operations follow the existing Jupid permissions for records in the business; they are not limited to API-source records. Demo users cannot call any of the four upload/delete operations.

Existing Jupid operations

The API also exposes the thirteen existing MCP operations. Paths below are relative to /api/v1/organizations/{organization_id}. Inputs and responses match their MCP operations, including existing owner-only restrictions. Account deletion requires the business owner. /billing reports the authenticated person's own billing status for either role; it does not expose the organization owner's billing.

HTTP requestOperation
GET /profilegetProfile
GET /accountslistAccounts
GET /categorieslistCategories
GET /transactionssearchTransactions
GET /transactions/{transaction_id}getTransaction
GET /counterpartiessearchCounterparties
GET /counterparties/{counterparty_id}getCounterparty
GET /reports/cashflowgetCashflowReport
GET /reports/pnlgetPnlReport
GET /outstanding-issueslistOutstandingIssues
POST /outstanding-issues/{id}/answersanswerOutstandingIssue
GET /agent-connectiongetAgentConnectionInfo
GET /billinggetBillingStatus

Billing status reports synced Stripe subscriptions. Partner-provided paid access is separate and is not reflected in this response.

GET inputs use query parameters with existing camelCase field names. Encode arrays as JSON, for example accountIds=["..."]; numbers and booleans are also JSON values, such as limit=20 and includeInactive=true. URL-encode these values. The answer endpoint accepts answer and optional selectedOption in a JSON body; the issue ID goes in the path.

Existing MCP OAuth access tokens can call the organization HTTP routes without X-Jupid-Partner; they cannot call partner connection. MCP authentication is unchanged. The four ingestion operations are also MCP tools, using the user's active Jupid business.

Errors

Errors return a JSON error string. Schema validation errors also include issues; malformed JSON returns an error without that list.

{
  "error": "Connected API source not found"
}
StatusAction
400Read the operation error, such as an unavailable source or ID belonging to another account.
401Supply valid authorization or refresh an expired token.
403Check the connected user, signed business, and current membership.
404Check the route.
413Send a smaller request.
415Set Content-Type: application/json on data POST requests.
422Correct the JSON or fields.
500Retry with backoff; contact Jupid if the same request keeps failing.

For support, include the HTTP method, path, status, and error message, along with your partner ID and environment. Do not send signing secrets or bearer tokens.

On this page