Skip to main content
Built for accounting apps, ERPs, and platforms that manage invoicing for many businesses under a single integration. Modeled on Stripe Connect: you authenticate with your own platform API key and name the tenant to act on with an Invoice-Account header — no per-tenant keys to store.

Model

platform organization            (your account, holds your sk_test_/sk_live_ keys)
  ├─ connected account org_A      (a client business — its own fiscal_profiles, invoices, downloads…)
  ├─ connected account org_B
  └─ connected account org_C
  • A platform is any organization. A connected account is an organization owned by it (parent). Each connected account is fully isolated: its invoices, fiscal profiles, downloads, events, and webhooks are scoped to it — the platform (and sibling accounts) can’t read them except by explicitly acting as that account.
  • One level only: a connected account cannot own connected accounts.
  • Connected accounts have no API keys. You always authenticate with the platform key.

1. Create connected accounts

Use your platform key, no header:
curl -X POST https://api.example.com/v1/accounts \
  -H "authorization: Bearer $PLATFORM_KEY" \
  -H 'content-type: application/json' \
  -d '{ "name": "Tortillería López SA de CV" }'
# → { "object": "account", "id": "org_...", "name": "...", "parent": "org_platform", ... }
GET /v1/accounts lists them (cursor-paginated); GET /v1/accounts/:id retrieves one.

2. Act on behalf of a connected account

Send your platform key plus the Invoice-Account header with the connected account id. Every endpoint then reads/writes that account’s data:
# Register the client's CSD under their account
curl -X POST https://api.example.com/v1/fiscal_profiles \
  -H "authorization: Bearer $PLATFORM_KEY" \
  -H "Invoice-Account: org_client123" \
  -F "legal_name=TORTILLERIA LOPEZ" -F "tax_regime=601" -F "zip=26015" \
  -F "csd_password=…" -F "csd_cer=@csd.cer" -F "csd_key=@csd.key"

# Stamp an invoice for that client
curl -X POST https://api.example.com/v1/invoices \
  -H "authorization: Bearer $PLATFORM_KEY" \
  -H "Invoice-Account: org_client123" \
  -H 'content-type: application/json' \
  -d '{ "fiscal_profile_id": "fp_...", "payment_form": "03", ... }'
Notes:
  • Prefer a query param? ?account=org_client123 works too; the header wins if both are sent.
  • Test vs live is still decided by the key (sk_test_ → sandbox, sk_live_ → live). A platform test key acting on a connected account operates on that account’s test data.
  • Naming your own platform org (or omitting the header) acts as the platform itself.
  • Acting on an account you don’t own returns 403 connect.account_not_authorized.

3. Manage a connected account

Rename, attach metadata, suspend, or delete an account with your platform key (no Invoice-Account header — you are managing the account, not acting as it):
# Rename + tag
curl -X PATCH https://api.example.com/v1/accounts/org_client123 \
  -H "authorization: Bearer $PLATFORM_KEY" -H 'content-type: application/json' \
  -d '{ "name": "Tortillería López (Norte)", "metadata": { "tier": "pro" } }'

# Suspend: any later attempt to act as this account is refused
curl -X PATCH https://api.example.com/v1/accounts/org_client123 \
  -H "authorization: Bearer $PLATFORM_KEY" -H 'content-type: application/json' \
  -d '{ "disabled": true }'
# Acting as it now returns 403 connect.account_disabled until you re-enable:
curl -X PATCH https://api.example.com/v1/accounts/org_client123 \
  -H "authorization: Bearer $PLATFORM_KEY" -H 'content-type: application/json' \
  -d '{ "disabled": false }'

# Delete
curl -X DELETE https://api.example.com/v1/accounts/org_client123 \
  -H "authorization: Bearer $PLATFORM_KEY"
# → { "object": "account", "id": "org_client123", "deleted": true }
metadata is string→string, merged on PATCH (send a key with an empty-string value to delete it).

4. Connect webhooks — every client’s events on one endpoint

Instead of registering a webhook per client, register one endpoint on the platform with connect: true. It receives events from all your connected accounts, and each delivery’s account field tells you which one:
curl -X POST https://api.example.com/v1/webhook_endpoints \
  -H "authorization: Bearer $PLATFORM_KEY" \
  -H 'content-type: application/json' \
  -d '{ "url": "https://your.app/webhooks", "enabled_events": ["invoice.stamped","invoice.cancelled"], "connect": true }'
Delivery body:
{
  "id": "evt_...",
  "type": "invoice.stamped",
  "created": "2026-07-10T18:00:00.000Z",
  "livemode": false,
  "account": "org_client123",
  "data": { ... the invoice ... }
}
A non-connect endpoint only receives the owning org’s own events. (Every delivery carries account; for a normal endpoint it’s just the org itself.)

Reading a connected account’s event log / resources

Use the same acting-as header: GET /v1/events -H "Invoice-Account: org_client123", GET /v1/invoices -H "Invoice-Account: org_client123", etc.

Replaying a delivery

If your endpoint was down, replay an event to your endpoints with POST /v1/events/:id/resend. An empty body re-fans-out to every matching endpoint; pass { "webhook_endpoint_id": "we_..." } to target one. For a connect endpoint, act with the same Invoice-Account header as the account that owns the event. See Webhooks.