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

# Quickstart

> Issue your first card in 5 steps

This guide walks you through the core Partner API flow: creating a cardholder, completing KYC, and issuing a card.

<Tip>
  **Prefer to run a working example?** <a href="/assets/demo.zip.png" download="demo.zip">Download the sample demo app</a> — a self-contained Bun + React project that walks through every step in this guide against the sandbox. Extract it, add your `CONTRO_API_KEY` to `.env`, and run `bun install && bun run dev`.
</Tip>

## Prerequisites

* A Contro partner account with sandbox API keys (`sk_test_*`)
* A tool for making HTTP requests (cURL, Postman)

<Steps>
  <Step title="Authenticate">
    Set your API key. All examples below use the sandbox key.

    <CodeGroup>
      ```bash cURL theme={null}
      export CONTRO_API_KEY="sk_test_your_key_here"
      ```

      ```typescript SDK (Node) theme={null}
      import Contro from "@contro/partner-sdk";

      const contro = new Contro({
        apiKey: "sk_test_your_key_here",
      });
      ```

      ```python SDK (Python) theme={null}
      from contro_partner_sdk import Contro

      contro = Contro(api_key="sk_test_your_key_here")
      ```

      ```typescript TypeScript (fetch) theme={null}
      const API_KEY = "sk_test_your_key_here";
      const BASE_URL = "https://api.contro.me/v1";

      const headers = {
        "x-contro-api-key": API_KEY,
        "Content-Type": "application/json",
      };
      ```
    </CodeGroup>
  </Step>

  <Step title="Create a KYC session">
    Register an end user by first creating a hosted KYC session. The user completes identity verification on the Contro-hosted page. See [Cardholders](/partner/cardholders) for all KYC options.

    <CodeGroup>
      ```bash cURL theme={null}
      curl -X POST https://api.contro.me/v1/partner/kyc-sessions \
        -H "x-contro-api-key: $CONTRO_API_KEY" \
        -H "Content-Type: application/json" \
        -d '{
          "externalUserId": "user_123"
        }'
      ```

      ```typescript SDK (Node) theme={null}
      const session = await contro.kycSessions.create({
        externalUserId: "user_123",
      });
      // session = { id: "ks_abc123", url: "https://...", expiresAt: "..." }
      // → Open session.url for the user to complete KYC
      ```

      ```python SDK (Python) theme={null}
      session = contro.kyc_sessions.create(external_user_id="user_123")
      # session = { id: "ks_abc123", url: "https://...", expires_at: "..." }
      # → Open session.url for the user to complete KYC
      ```

      ```typescript TypeScript (fetch) theme={null}
      const session = await fetch(`${BASE_URL}/partner/kyc-sessions`, {
        method: "POST",
        headers,
        body: JSON.stringify({ externalUserId: "user_123" }),
      }).then((r) => r.json());
      // session = { id: "ks_abc123", url: "https://...", expiresAt: "..." }
      // → Open session.url for the user to complete KYC
      ```
    </CodeGroup>

    Once the session status is `completed` (poll or listen for the `kyc_session.completed` webhook), create the cardholder:

    <CodeGroup>
      ```bash cURL theme={null}
      curl -X POST https://api.contro.me/v1/partner/cardholders \
        -H "x-contro-api-key: $CONTRO_API_KEY" \
        -H "Content-Type: application/json" \
        -d '{
          "externalUserId": "user_123",
          "kycSource": "web",
          "kycSessionId": "ks_abc123",
          "email": "jane@example.com",
          "phoneNumber": "+14155552671"
        }'
      ```

      ```typescript SDK (Node) theme={null}
      const cardholder = await contro.cardholders.create({
        externalUserId: "user_123",
        kycSource: "web",
        kycSessionId: session.id,
        email: "jane@example.com",
        phoneNumber: "+14155552671",
      });

      console.log(cardholder.id); // "ch_abc123"
      ```

      ```python SDK (Python) theme={null}
      cardholder = contro.cardholders.create(
          external_user_id="user_123",
          kyc_source="web",
          kyc_session_id=session.id,
          email="jane@example.com",
          phone_number="+14155552671",
      )

      print(cardholder.id)  # "ch_abc123"
      ```

      ```typescript TypeScript (fetch) theme={null}
      const cardholder = await fetch(`${BASE_URL}/partner/cardholders`, {
        method: "POST",
        headers,
        body: JSON.stringify({
          externalUserId: "user_123",
          kycSource: "web",
          kycSessionId: session.id,
          email: "jane@example.com",
          phoneNumber: "+14155552671",
        }),
      }).then((r) => r.json());

      console.log(cardholder.id); // "ch_abc123"
      ```
    </CodeGroup>
  </Step>

  <Step title="Initiate KYC for a card program">
    Submit the cardholder for KYC verification against a specific card program. Cards cannot be issued until KYC is approved for that program.

    <CodeGroup>
      ```bash cURL theme={null}
      curl -X POST https://api.contro.me/v1/partner/cardholders/ch_abc123/kyc \
        -H "x-contro-api-key: $CONTRO_API_KEY" \
        -H "Content-Type: application/json" \
        -d '{
          "cardProgramId": "cp_xyz789"
        }'
      ```

      ```typescript SDK (Node) theme={null}
      await contro.cardholders.initiateKyc(cardholder.id, {
        cardProgramId: "cp_xyz789",
      });
      ```

      ```python SDK (Python) theme={null}
      contro.cardholders.initiate_kyc(cardholder.id, card_program_id="cp_xyz789")
      ```

      ```typescript TypeScript (fetch) theme={null}
      await fetch(`${BASE_URL}/partner/cardholders/${cardholder.id}/kyc`, {
        method: "POST",
        headers,
        body: JSON.stringify({
          cardProgramId: "cp_xyz789",
        }),
      });
      ```
    </CodeGroup>

    Poll the KYC status or listen for the webhook event:

    <CodeGroup>
      ```bash cURL theme={null}
      curl "https://api.contro.me/v1/partner/cardholders/ch_abc123/kyc?cardProgramId=cp_xyz789" \
        -H "x-contro-api-key: $CONTRO_API_KEY"
      ```

      ```typescript SDK (Node) theme={null}
      const kyc = await contro.cardholders.getKyc(cardholder.id, {
        cardProgramId: "cp_xyz789",
      });

      console.log(kyc.kycStatus); // "pending" | "approved" | "rejected"
      ```

      ```python SDK (Python) theme={null}
      kyc = contro.cardholders.get_kyc(cardholder.id, card_program_id="cp_xyz789")

      print(kyc.kyc_status)  # "pending" | "approved" | "rejected"
      ```

      ```typescript TypeScript (fetch) theme={null}
      const kyc = await fetch(
        `${BASE_URL}/partner/cardholders/${cardholder.id}/kyc?cardProgramId=cp_xyz789`,
        { headers }
      ).then((r) => r.json());

      console.log(kyc.kycStatus); // "pending" | "approved" | "rejected"
      ```
    </CodeGroup>
  </Step>

  <Step title="Issue a card">
    Once KYC is approved for the card program, issue a card.

    <CodeGroup>
      ```bash cURL theme={null}
      curl -X POST https://api.contro.me/v1/partner/cards \
        -H "x-contro-api-key: $CONTRO_API_KEY" \
        -H "Content-Type: application/json" \
        -d '{
          "cardholderId": "ch_abc123",
          "programId": "cp_xyz789"
        }'
      ```

      ```typescript SDK (Node) theme={null}
      const card = await contro.cards.create({
        cardholderId: cardholder.id,
        programId: "cp_xyz789",
      });

      console.log(card.id); // "card_def456"
      ```

      ```python SDK (Python) theme={null}
      card = contro.cards.create(
          cardholder_id=cardholder.id,
          program_id="cp_xyz789",
      )

      print(card.id)  # "card_def456"
      ```

      ```typescript TypeScript (fetch) theme={null}
      const card = await fetch(`${BASE_URL}/partner/cards`, {
        method: "POST",
        headers,
        body: JSON.stringify({
          cardholderId: cardholder.id,
          programId: "cp_xyz789",
        }),
      }).then((r) => r.json());

      console.log(card.id); // "card_def456"
      ```
    </CodeGroup>
  </Step>

  <Step title="Query transactions">
    View transaction activity for the card.

    <CodeGroup>
      ```bash cURL theme={null}
      curl https://api.contro.me/v1/partner/cards/card_def456/transactions \
        -H "x-contro-api-key: $CONTRO_API_KEY"
      ```

      ```typescript SDK (Node) theme={null}
      const txns = await contro.cards.listTransactions(card.id);

      for (const tx of txns.data) {
        console.log(`${tx.type} ${tx.amount} ${tx.currency} - ${tx.merchant}`);
      }
      ```

      ```python SDK (Python) theme={null}
      txns = contro.cards.list_transactions(card.id)

      for tx in txns.data:
          print(f"{tx.type} {tx.amount} {tx.currency} - {tx.merchant}")
      ```

      ```typescript TypeScript (fetch) theme={null}
      const txns = await fetch(
        `${BASE_URL}/partner/cards/${card.id}/transactions`,
        { headers }
      ).then((r) => r.json());

      for (const tx of txns.data) {
        console.log(`${tx.type} ${tx.amount} ${tx.currency} - ${tx.merchant}`);
      }
      ```
    </CodeGroup>
  </Step>
</Steps>

## Next steps

<CardGroup cols={2}>
  <Card title="Cardholders" icon="user" href="/partner/cardholders">
    Learn about cardholder management and KYC methods
  </Card>

  <Card title="Cards" icon="credit-card" href="/partner/cards">
    Card lifecycle, limits, and actions
  </Card>

  <Card title="Webhooks" icon="bell" href="/partner/webhooks">
    Set up real-time event notifications
  </Card>

  <Card title="API reference" icon="code" href="/api-reference/partner/introduction">
    Explore all endpoints in the playground
  </Card>

  <Card title="Sample demo app" icon="download" href="/assets/demo.zip.json">
    Download a runnable end-to-end example against the sandbox
  </Card>
</CardGroup>
