Building an integration

An integration is an application CardNexus has approved to act on its users' accounts — an inventory-sync platform, a deck builder, a collection manager. Instead of each of your users minting and pasting an API key, your application holds one credential and names the account it's acting on in a header.

This guide covers connecting an account, acting on its behalf, receiving webhooks, and creating accounts for sellers who aren't on CardNexus yet.

Requesting an application

Applications are created by CardNexus — there is no self-serve sign-up. Email partnerships@cardnexus.com and tell us what you're building: what your integration does, why it needs to act on CardNexus accounts, and which parts of an account it needs to read or write. CardNexus creates applications for the use cases it considers valid, and grants each one only the permissions that use case needs.

Getting set up

CardNexus mints your application and gives you:

  • an application credential (cni_live_…) — treat it like a password,
  • your application slug (used in the connect link),
  • one webhook endpoint you configure in your Svix portal.

You keep one credential for your whole platform. It is not tied to any one account.

From then on you manage the application yourself, under Settings → Integrations on cardnexus.com: create and revoke credentials, register the redirect URIs the connect flow may return your users to, open your webhooks portal, and read the rate limits that apply to each connected account.

Connecting an account

You connect an account with an approve-link handshake:

  1. Send the user to https://cardnexus.com/connect/{slug}?redirect_uri={your_callback}&state={state}.
    • redirect_uri must exactly match one of your registered URIs.
    • state is an opaque value you generate per attempt and bind to the user's session. CardNexus returns it unchanged; reject any callback whose state you don't recognise. This is your CSRF protection.
  2. The user signs in, reviews the permissions your application is asking for, and approves.
  3. CardNexus redirects to your redirect_uri with ?code={code}&state={state} (or ?error=access_denied&state={state} if they decline).
  4. Exchange the code, authenticated with your credential:
POST /v1/connect/exchange
Authorization: Bearer cni_live_…
Content-Type: application/json

{ "code": "…" }
{
  "accountId": "65f3a2b1c8d4e9f7a6b5c4d3",
  "scopes": ["inventory:read", "inventory:write"],
  "grantedAt": "2026-02-03T18:42:55.000Z"
}

The code is single-use and expires 5 minutes after it's issued. Store the accountId — it's what you send on every request for that account.

Acting on an account

Send your credential and the account id on each request:

GET /v1/inventory
Authorization: Bearer cni_live_…
CardNexus-Account: 65f3a2b1c8d4e9f7a6b5c4d3

Every /v1 endpoint works exactly as documented; the account id in the header is the only difference from a first-party key. You can only do what the account granted you — a request outside your granted permissions returns 403.

Your application's permissions are listed under Settings → Integrations. To change what it may request, email partnerships@cardnexus.com with the permissions you need and what they're for. Widening an application's permissions never widens the grants accounts have already given it — connected accounts approve the new set the next time they go through the connect flow.

If an account hasn't connected your application (or disconnected it), requests for it return 403. A user disconnects your application from their CardNexus Settings → Integrations at any time; the next request stops working.

Webhooks

Configure one endpoint in your Svix portal. Every connected account's events are delivered there. Each payload carries an account field telling you which account the event belongs to:

{
  "type": "order.created",
  "eventId": "…",
  "timestamp": "2026-02-03T18:42:55.000Z",
  "account": "65f3a2b1c8d4e9f7a6b5c4d3",
  "data": { }
}

Verify the Svix signature with the official libraries, allow a 5-minute timestamp tolerance, and treat (account, eventId) as the idempotency key. You only receive event families your granted permissions cover. When a user disconnects your application, you stop receiving that account's events; deliveries already queued may still retry for a short window, so ignore events for any account you no longer have access to.

Creating accounts for new sellers

If CardNexus has granted your application the managed-account privilege, you can create an account for a seller who isn't on CardNexus yet:

POST /v1/accounts
Authorization: Bearer cni_live_…
Content-Type: application/json

{ "email": "seller@shop.example", "country": "FR" }
{ "accountId": "65f3a2b1c8d4e9f7a6b5c4d3", "username": "north_arena_tcg", "status": "staged" }

A staged account can hold inventory immediately, but its listings can't be published until the seller completes onboarding. Send them there with a hosted-onboarding link:

POST /v1/account/onboarding-link
Authorization: Bearer cni_live_…
CardNexus-Account: 65f3a2b1c8d4e9f7a6b5c4d3
Content-Type: application/json

{ "returnUrl": "https://your-platform.example/cardnexus/done" }

Send the seller to the returned url. They accept the CardNexus terms and set up payouts in their own browser, then return to your returnUrl. That's what takes their listings live. Until then, POST to the listings endpoints returns an error — sync inventory and publish once onboarding is done. Poll GET /v1/account/me for the current state.

If the email already belongs to an account, POST /v1/accounts returns ACCOUNT_EXISTS — send that seller through the normal connect flow instead.

There is a daily limit on how many accounts you can create. Once you reach it, POST /v1/accounts returns QUOTA_EXCEEDED with the limit you hit and the resetAt timestamp it lifts at. The count resets at midnight UTC.