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:
-
Send the user to
https://cardnexus.com/connect/{slug}?redirect_uri={your_callback}&state={state}.redirect_urimust exactly match one of your registered URIs.-
stateis an opaque value you generate per attempt and bind to the user's session. CardNexus returns it unchanged; reject any callback whosestateyou don't recognise. This is your CSRF protection.
- The user signs in, reviews the permissions your application is asking for, and approves.
-
CardNexus redirects to your
redirect_uriwith?code={code}&state={state}(or?error=access_denied&state={state}if they decline). - 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.