> ## Documentation Index
> Fetch the complete documentation index at: https://imprint-b3c5d5af-partner-docs-latest.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Account Linking

> Connect your customers' loyalty or identity accounts with their Imprint card account

Account Linking lets you connect a customer's identity in your system (e.g. loyalty account, membership) to their Imprint card account. Once linked, Imprint can credit partner-owned rewards and display membership status to the member.

Account Linking uses **OpenID Connect (OIDC)**. Imprint acts as an OIDC relying party against your identity provider: Imprint redirects the member to your authorization endpoint; after they authenticate, you redirect back to Imprint with an authorization code. Imprint exchanges that code at your token endpoint for an **ID token**, verifies it, reads the customer's identity from it, and creates the link.

<Info>
  **OIDC and OAuth 2.0 — how to read this page.** OIDC is not an alternative to OAuth 2.0; it is the identity layer standardized *on top of* OAuth 2.0. So OAuth 2.0 terms appear throughout this page — the authorization code grant, the token endpoint, PKCE. In every case they refer to the OAuth 2.0 mechanism **as profiled by OIDC**, not to a separate OAuth-only integration.

  | Term you'll see | Defined by | Role in this integration |
  | - | - | - |
  | Authorization code grant | OAuth 2.0 | The redirect-and-exchange shape that OIDC's Authorization Code Flow profiles |
  | Token endpoint / client ID / client secret | OAuth 2.0 | Standard client registration; OIDC reuses it unchanged |
  | PKCE (`S256`) | OAuth 2.0 extension (RFC 7636) | Hardens the code exchange; inherited by OIDC |
  | ID token, `sub` claim, discovery document, JWKS | OIDC | What Imprint actually uses to establish **identity** |

  Only the last row conveys identity. That is why Imprint requires OIDC: an OAuth 2.0 access token on its own proves authorization, not who the member is.
</Info>

## What you need to provide

To set up Account Linking, provide the following to Imprint for **each environment** (sandbox and production):

### 1. OIDC client application

Register an OIDC client application for Imprint in your identity provider (or provide credentials to an existing one). These are the standard OAuth 2.0 client-registration fields that OIDC reuses unchanged. **The client ID and both endpoints are always required; the client secret only for confidential clients.** Send the endpoints even if you publish a discovery document — Imprint configures them explicitly rather than reading them from discovery:

| Item | Description |
| - | - |
| **Client ID** | The client identifier issued to Imprint |
| **Client Secret** | Required for a confidential client. Not needed for a public client (PKCE-only). |
| **Authorization endpoint** | Your login/authorize URL where members are redirected to sign in |
| **Token endpoint** | Your token exchange URL where Imprint exchanges the authorization code for an ID token |

<Info>
  Please send credentials over a secure medium (e.g. a secrets manager, encrypted channel). Do not send client secrets in plain text over email.
</Info>

### 2. Redirect URIs

Configure your client application to allow-list the following redirect URIs. These are where your system redirects members back to Imprint after authentication.

**Sandbox:**

* `https://apply.sbx.imprint.co/link-account/{merchant_key}`
* `https://account.sbx.imprint.co/link-account/{merchant_key}`
* `imprint-sbx://link-account` (mobile deep link)

**Production:**

* `https://apply.imprint.co/link-account/{merchant_key}`
* `https://account.imprint.co/link-account/{merchant_key}`
* `imprint://link-account` (mobile deep link)

<Note>
  Replace `{merchant_key}` with your assigned merchant key (provided by Imprint during onboarding).
</Note>

<h3 id="identity-resolution">
  3\. Identity resolution
</h3>

Imprint resolves the authenticated member's identity from the **OIDC ID token** returned by your token endpoint. Provide:

* Your **OIDC discovery URL** (`.well-known/openid-configuration`). Imprint reads your **issuer** and **`jwks_uri`** from it to verify ID token signatures. It does not source the authorization or token endpoints from discovery — those are the separately configured values in [section 1](#1-oidc-client-application), which remain required.
* The **`openid` scope**, which must be grantable to Imprint's client. Include any additional scopes your provider requires to issue an ID token.
* A **stable, non-reassignable identifier** in the ID token's `sub` claim. This becomes the partner customer ID Imprint stores against the link, so it must not change if the member updates their email, username, or phone number.
* **`nonce` echoed into the ID token.** Imprint sends a single-use `nonce` on the authorization request; your provider must return it unchanged as the ID token's `nonce` claim. Imprint rejects any ID token whose `nonce` is missing, empty, or does not match — this is the most common cause of a flow that authenticates successfully but fails at the final step.
* Your **client authentication method** at the token endpoint: `client_secret_post` (confidential client) or `none` (public client, PKCE required).

<Note>
  Imprint reads identity **only** from the verified ID token's `sub` claim. Imprint does not call a separate userinfo or custom identity endpoint to determine who the member is, so no such endpoint is required. If your provider cannot issue an ID token with a stable `sub`, contact Imprint before starting integration.
</Note>

### 4. PKCE support

Confirm whether your token endpoint supports **PKCE** (Proof Key for Code Exchange, [RFC 7636](https://datatracker.ietf.org/doc/html/rfc7636)) with the `S256` challenge method. Imprint configures PKCE per product as either `S256` or disabled.

PKCE is an OAuth 2.0 extension rather than part of the OIDC specification, but it applies to the OIDC Authorization Code Flow exactly as it does to a plain OAuth 2.0 one — OIDC inherits it. When `S256` is configured for your product, Imprint sends `code_challenge` / `code_challenge_method=S256` on the authorization request and `code_verifier` on the token exchange; when PKCE is disabled, it sends none of these. PKCE is **required for public clients** (`none` client authentication) and strongly preferred for confidential clients, as it protects against authorization code interception.

### 5. Test credentials

Provide a **test member** (or test tenant) in your identity provider so Imprint can complete end-to-end testing of the redirect, token exchange, ID token verification, and identity resolution.

<h3 id="framing-requirements-web-sdk">
  6\. Framing requirements (Web SDK)
</h3>

When the member links their account from the [Web SDK](/web), the entire experience — including the redirect to your authorization/login page — runs inside an **`<iframe>`** embedded on the partner page. Your authorization endpoint, and **every page in the redirect chain** (login page, consent screen, any interstitial or identity-provider hosted page), must therefore be allowed to render inside a frame.

<Warning>
  A page that returns **`X-Frame-Options: DENY`** or **`X-Frame-Options: SAMEORIGIN`**, or a **`Content-Security-Policy: frame-ancestors`** directive that excludes Imprint's origin, will be blocked by the browser. The member sees a blank frame, the sign-in flow cannot complete, and **the SDK cannot detect or report the failure** — it looks like the flow silently hung.
</Warning>

To support the Web SDK, explicitly allow Imprint's Web SDK origin on every page in your sign-in flow via a CSP `frame-ancestors` directive. Do **not** use `X-Frame-Options`, which has no allow-list mechanism beyond `SAMEORIGIN` and cannot permit a cross-origin frame:

**Sandbox:**

```http theme={null}
Content-Security-Policy: frame-ancestors https://apply.sbx.imprint.co;
```

**Production:**

```http theme={null}
Content-Security-Policy: frame-ancestors https://apply.imprint.co;
```

<Note>
  When both `X-Frame-Options` and CSP `frame-ancestors` are present, modern browsers honor `frame-ancestors` and ignore `X-Frame-Options`. However, some environments and security proxies still enforce `X-Frame-Options`, so the safest configuration is to **remove `X-Frame-Options` entirely** on the pages in your linking flow and rely on `frame-ancestors`.
</Note>

These framing requirements — the `frame-ancestors` allow-list and the `X-Frame-Options` caveats above — apply **only to the Web SDK**. The [iOS](/ios) and [Android](/android) SDKs present the linking flow in a native view (and use the `imprint://` / `imprint-sbx://` deep links), so they are not framed and are unaffected by `X-Frame-Options` / CSP `frame-ancestors` headers.

## How the flow works

<Steps>
  <Step title="User initiates linking">
    The member taps "Link Account" in the Imprint app. Imprint generates a secure `state` token and a single-use `nonce`, then redirects the user to your authorization endpoint with both.
  </Step>

  <Step title="User authenticates">
    The user signs in on your login page (or an existing session is reused). Your system redirects back to Imprint's redirect URI with an authorization `code` and `state`.
  </Step>

  <Step title="Token exchange">
    Imprint exchanges the authorization code at your token endpoint for an **ID token** (sending the PKCE `code_verifier` when `S256` is configured), then verifies the token's signature against your JWKS along with its issuer, audience, expiry, and `nonce`.
  </Step>

  <Step title="Identity resolution">
    Imprint reads the partner customer ID from the `sub` claim of the verified ID token.
  </Step>

  <Step title="Link created">
    The customer's Imprint account is linked to their partner identity. A `CUSTOMER_LINK` webhook event is fired to notify your systems.
  </Step>
</Steps>

## What we validate in testing

Once configuration is complete, Imprint will verify:

1. Your OIDC discovery document is reachable and returns an `issuer` and a `jwks_uri`.
2. Redirect to your authorization endpoint succeeds and returns `code` + `state`.
3. Token exchange succeeds (with the PKCE `code_verifier` when `S256` is configured) and returns an ID token.
4. The ID token verifies — signature against your JWKS, plus issuer, audience, expiry, and a `nonce` matching the one Imprint sent.
5. The `sub` claim yields a valid, stable partner customer ID.
6. The link is created and the `CUSTOMER_LINK` webhook fires successfully.

## Webhook notification

After a successful link, Imprint sends a [`CUSTOMER_LINK`](/event-notifications) webhook event containing the linked customer details. Subscribe to this event to update your systems when a member links their account.

## Changing a linked partner customer ID

If the partner customer account a member is linked to changes (for example, after you merge two loyalty accounts), update the existing link with [`PATCH /v2/customers/{customer_id}/link`](https://docs.imprint.co/api-reference/customer-links/update-the-partner-customer-id-of-a-customer-link) instead of creating a new one.

```bash theme={null}
curl -X PATCH https://dev.sbx.imprint.co/v2/customers/{customer_id}/link \
  -u YOUR_API_KEY: \
  -H "Content-Type: application/json" \
  -d '{
    "partner_customer_id": "NEW_PARTNER_USER_789",
    "metadata": { "store_id": "store_789" }
  }'
```

* `partner_customer_id` is required and replaces the value on the customer's active link. `metadata` is optional and is appended the same way as when creating a link.
* The response is the updated `CustomerLink`. A `CUSTOMER_LINK` webhook is sent with the new `partner_customer_id`.
* If `partner_customer_id` already matches the active link, the link is returned unchanged, `metadata` is ignored, and no webhook is sent.
* If the customer does not exist or has no active link, the request returns `404`. Create the link with `POST /v2/customers/{customer_id}/link` first.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.