Skip to main content

The MarginFront API (one surface, unified)

Heads up: this doc used to describe two parallel API surfaces — the SDK API and the admin REST API — and tell you which to pick. Those two surfaces have been unified. There is now one public API under /v1/<resource>. This doc is kept around as a migration reference for anyone who was using the old routes.

The unified API (what to use today)

All public endpoints live under /v1/<resource>:
/v1/verify
/v1/customers        /v1/customers/:id
/v1/agents           /v1/agents/:id
/v1/signals          /v1/signals/:id
/v1/pricing-plans    /v1/pricing-plans/:id
/v1/subscriptions    /v1/subscriptions/:id
/v1/usage/record
/v1/analytics/usage
/v1/invoices         /v1/invoices/:id
/v1/portal-sessions  /v1/portal-sessions/:id
Key points:
  • No org/:orgId/ in the URL. The API key alone identifies your organization. You never need to pass an org ID in a URL anywhere.
  • No /sdk/ prefix either. What used to be “SDK-only” endpoints now live at the same top level as everything else.
  • One credential per request. Pass your API key via the x-api-key header. See authentication.md for the full details.
  • Every endpoint is the same shape whether you call it from curl, the Node SDK, a Python backend, or anything else. The SDK is now a thin wrapper over the exact URLs documented in these files.
Example:
curl https://api.marginfront.com/v1/customers \
  -H "x-api-key: $MF_API_SECRET_KEY"

Legacy paths (still working during the deprecation window)

Two legacy URL shapes still resolve, backed by the same handlers as the new URLs:
  • /v1/org/:orgId/<resource> — the old admin REST shape
  • /v1/sdk/<resource> — the old SDK-surface shape
Both still work. The @marginfront/sdk@0.5.0 npm package continues to function without any changes on your side. Existing integrations that hardcoded either URL shape will keep working. But:
  • The legacy paths are not documented in the per-resource doc files. Those files only describe the new canonical paths.
  • The legacy paths will be removed after the deprecation window ends. When we pick a removal date, we’ll announce it with enough runway for you to migrate.
  • New integrations should always use /v1/<resource>. It’s shorter, cleaner, and will outlive the legacy paths.

Migrating existing integrations

If you’re still on the old URL shapes, migration is a find-and-replace:
BeforeAfter
POST /v1/org/{orgId}/customersPOST /v1/customers
GET /v1/sdk/customersGET /v1/customers
POST /v1/sdk/usage/recordPOST /v1/usage/record
GET /v1/sdk/analytics/usageGET /v1/analytics/usage
GET /v1/sdk/invoicesGET /v1/invoices
GET /v1/sdk/verifyGET /v1/verify
POST /v1/sdk/portal-sessionsPOST /v1/portal-sessions
Authorization: Bearer mf_sk_...x-api-key: mf_sk_...
Also: drop any MF_ORG_ID env var you were passing into URLs. You don’t need it anymore.

Why the unification happened

Historical reasons. Originally we had:
  1. An admin REST API (/v1/org/:orgId/*) that the dashboard used internally. It required the URL to include the org ID because the dashboard knew which org the logged-in user was viewing.
  2. An SDK API (/v1/sdk/*) that the @marginfront/sdk npm package called. It didn’t need the org ID in the URL because the API key already identified the org.
When we opened the admin endpoints to API key auth (so customers could use them directly from their own code), customers ended up with two parallel surfaces to choose from. Having two was confusing, shipped too much surface area to document, and made the SDK harder to build and maintain. The unify decision (2026-04-11) collapsed both into a single public API under /v1/<resource>, matching how Stripe, Anthropic, OpenAI, Resend, and every modern API-first company does it:
  • URL stability. The URL shape doesn’t change based on account state.
  • Less to remember. One credential, one URL shape.
  • Fewer mistakes. You can’t accidentally hit one org’s URL with another org’s key.
  • Cleaner SDKs. No orgId parameter cluttering every method signature.

Questions?

Email team@marginfront.com. If something in the old docs still works but isn’t documented in the new ones, that’s a migration gap we want to know about.