> ## Documentation Index
> Fetch the complete documentation index at: https://docs.machines.cash/llms.txt
> Use this file to discover all available pages before exploring further.

# Crossmint

> Integrate Machines Partner API with Crossmint embedded wallets.

<Info>
  Check out working demo: [Live Demo](https://crossmint-machines-cards-demo.vercel.app/)\
  Repo: [crossmint-machines-cards-example](https://github.com/machines-cash/crossmint-machines-cards-example)

  [![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fmachines-cash%2Fcrossmint-machines-cards-example)
</Info>

This guide is for partners already using Crossmint embedded wallets.

Scope:

* Partner API integration (`/partner/v1`) only.
* Crossmint for auth + embedded wallet UX.
* Your backend holds partner credentials and calls Machines.
* Simple EVM-first path for create card, deposits, and withdrawals.

## Architecture

* Frontend uses Crossmint for sign-in and wallet creation.
* Frontend calls your backend only.
* Your backend calls Machines Partner API.
* Your backend executes withdrawal transactions with the embedded wallet signer path you control.

```mermaid theme={null}
sequenceDiagram
  participant User as User
  participant Frontend as Partner App (Frontend)
  participant Backend as Partner App (Backend)
  participant Crossmint as Crossmint
  participant Machines as Machines Partner API
  participant Chain as Blockchain

  User->>Frontend: Sign in with email
  Frontend->>Crossmint: Create embedded wallet
  Frontend->>Backend: POST /api/machines/session
  Backend->>Machines: POST /partner/v1/users/resolve
  Backend->>Machines: POST /partner/v1/sessions
  Backend-->>Frontend: partner session token
  Frontend->>Backend: KYC/cards/deposits/withdrawals requests
  Backend->>Machines: Partner API calls
  Backend->>Chain: send withdrawal tx when needed
```

## Prerequisites

* Crossmint project with:
  * client key for frontend
  * server key for backend (if you run server-side wallet operations)
  * embedded EVM wallet creation enabled
* Machines partner API key
* A backend service that can securely store:
  * `X-Partner-Key`
  * Crossmint server credentials (if used)

## Crossmint Embedded Wallet Checklist

Before sending traffic:

1. Wallet creation
   * New users get an embedded EVM wallet at login.
   * You can read the wallet address after auth.
2. Stable mapping
   * You persist partner `userId` + Crossmint user id + wallet address.
   * You always send the same mapped user identity to Machines.
3. Session bootstrap server-side
   * `POST /partner/v1/users/resolve` and `POST /partner/v1/sessions` happen on your backend.
   * `X-Partner-Key` is never exposed to browser code.
4. Browser request path
   * Browser calls your backend routes, not Machines directly.
   * This avoids CORS failures and secret leakage.
5. Withdrawal execution
   * Use one signer path consistently.
   * Persist tx ids/hashes and reconcile status.

## Recommended Data to Persist

Per user:

* `machinesUserId`
* `crossmintUserId`
* `embeddedWalletAddress`
* `linkedAt` / `lastUsedAt`

Per session:

* `sessionId`
* `scopes`
* `expiresAt`

Per withdrawal execution:

* idempotency key
* execution path (`controller_v1` or `coordinator_v2`)
* submitted tx id/hash
* confirmed tx hash

## End-to-End Partner Flow

### 1) Set up Crossmint providers (frontend)

```tsx theme={null}
"use client";

import {
  CrossmintProvider,
  CrossmintAuthProvider,
  CrossmintWalletProvider,
} from "@crossmint/client-sdk-react-ui";

export function Providers({ children }: { children: React.ReactNode }) {
  return (
    <CrossmintProvider apiKey={process.env.NEXT_PUBLIC_CROSSMINT_API_KEY!}>
      <CrossmintAuthProvider>
        <CrossmintWalletProvider
          createOnLogin={{
            chain: "base-sepolia",
            signer: { type: "email" },
          }}
        >
          {children}
        </CrossmintWalletProvider>
      </CrossmintAuthProvider>
    </CrossmintProvider>
  );
}
```

### 2) Resolve user + create partner session (backend)

```ts theme={null}
import { NextRequest, NextResponse } from "next/server";

const MACHINES_BASE_URL = process.env.MACHINES_PARTNER_BASE_URL || "https://dev-api.machines.cash/partner/v1";

export async function POST(req: NextRequest) {
  const { externalUserId, walletAddress } = await req.json();

  const resolveRes = await fetch(`${MACHINES_BASE_URL}/users/resolve`, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "X-Partner-Key": process.env.MACHINES_PARTNER_API_KEY!,
    },
    body: JSON.stringify({
      userId: externalUserId,
      wallet: { chain: "evm", address: walletAddress },
      walletLabel: "Crossmint Embedded Wallet",
    }),
  });

  if (!resolveRes.ok) {
    return NextResponse.json({ ok: false, error: "resolve_failed" }, { status: 502 });
  }

  const sessionRes = await fetch(`${MACHINES_BASE_URL}/sessions`, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "X-Partner-Key": process.env.MACHINES_PARTNER_API_KEY!,
    },
    body: JSON.stringify({
      userId: externalUserId,
      wallet: { chain: "evm", address: walletAddress },
      scopes: [
        "kyc.read",
        "kyc.write",
        "agreements.read",
        "agreements.write",
        "cards.read",
        "cards.write",
        "deposits.read",
        "deposits.write",
        "withdrawals.read",
        "withdrawals.write",
        "transactions.read",
      ],
      ttlSeconds: 900,
    }),
  });

  const payload = await sessionRes.json();
  if (!sessionRes.ok || !payload?.ok) {
    return NextResponse.json({ ok: false, error: "session_failed", payload }, { status: 502 });
  }

  return NextResponse.json({
    ok: true,
    sessionToken: payload.data.sessionToken,
    sessionId: payload.data.sessionId,
    expiresAt: payload.data.expiresAt,
  });
}
```

### 3) Call Machines through your backend proxy

Use the partner session token from step 2.

At minimum, support these routes:

* `GET /partner/v1/kyc/values`
* `POST /partner/v1/kyc/applications`
* `GET /partner/v1/kyc/status`
* `GET /partner/v1/agreements`
* `POST /partner/v1/agreements`
* `GET /partner/v1/cards`
* `POST /partner/v1/cards`
* `GET /partner/v1/deposits/assets`
* `POST /partner/v1/deposits/range`
* `POST /partner/v1/deposits/estimate`
* `POST /partner/v1/deposits`
* `GET /partner/v1/transactions`

### 4) Create withdrawals

Call sequence:

1. `GET /partner/v1/withdrawals/assets`
2. `POST /partner/v1/withdrawals/range`
3. `POST /partner/v1/withdrawals/estimate`
4. `POST /partner/v1/withdrawals`

Create request example:

```bash theme={null}
curl --request POST \
  --url https://api.machines.cash/partner/v1/withdrawals \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer <PARTNER_SESSION_TOKEN>' \
  --header 'Idempotency-Key: wdrl-001' \
  --data '{
    "amountCents": 2500,
    "source": {
      "contractId": "optional-uuid"
    },
    "destination": {
      "currency": "hbar",
      "network": "hbar",
      "address": "0.0.123456",
      "extraId": "optional-tag"
    },
    "adminAddress": "0xabc..."
  }'
```

### 5) Execute withdrawal transaction

Use response fields:

* `execution.callTarget`
* `execution.callPath`
* `parameters`

Rules:

* send tx to `execution.callTarget`
* sender must match `adminAddress` used in create request
* if status is `pending`, retry create with the same idempotency key

## Execution Model and Contract Call Paths

Machines withdrawal create response includes:

* `execution.callTarget`
* `execution.callPath` (`controller_v1` or `coordinator_v2`)
* `parameters` (7-arg withdrawal payload)

Contract paths:

* `controller_v1`: call 7-arg `withdrawAsset(...)`
* `coordinator_v2`: build admin typed-data signature, then call 10-arg `withdrawAsset(...)`

## Common Integration Pitfalls

* Calling Machines directly from browser code.
  * Use your backend proxy for all partner calls.
* Creating sessions without wallet context.
  * Always pass wallet data when resolving users/sessions.
* Not reusing idempotency keys on pending withdrawals.
  * Retry with the same `Idempotency-Key`.
* Executing on the wrong target contract.
  * Always use `execution.callTarget` from response.

## Further Reading

* Crossmint auth quickstart:
  * [https://docs.crossmint.com/authentication/quickstart](https://docs.crossmint.com/authentication/quickstart)
* Crossmint wallets React quickstart:
  * [https://docs.crossmint.com/wallets/quickstarts/react](https://docs.crossmint.com/wallets/quickstarts/react)
* Crossmint wallets overview:
  * [https://docs.crossmint.com/wallets/overview](https://docs.crossmint.com/wallets/overview)
* Machines partner docs:
  * [https://docs.machines.cash/partners/](https://docs.machines.cash/partners/)
