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

# Quickstart ⚡

> Register users and upload documents to enable regulated product access.

<CardGroup cols={2}>
  <Snippet file="before-you-start.mdx" />
</CardGroup>

<Snippet file="enterprise-feature.mdx" />

This quickstart guide walks you through registering a user, uploading documents, and fetching document details using Crossmint's APIs. These steps enable your users to access regulated products like onramp, offramp, and payouts.

<Steps>
  <Step title="User accepts Crossmint's Privacy Policy">
    Before sharing any user KYC data with Crossmint, you must ensure your users have accepted Crossmint's [Privacy Policy](https://www.crossmint.com/legal/privacy-policy). This is a prerequisite for companies sharing their customer's KYC data with Crossmint.

    <Accordion title="Requirements for collecting consent">
      Consent must be obtained following the guidelines below:

      1. The user must have the ability to see the terms they must accept
      2. The user must grant consent with a checkbox, unchecked by default
      3. The user doesn't need to open the hyperlink to the privacy policy to view the terms
      4. The user needs to see Crossmint's logo in the flow
      5. The specific text that you must present to your end user is: **"I consent to my KYC data being processed by Crossmint in accordance with its [Privacy Policy](https://www.crossmint.com/legal/privacy-policy)"**
      6. The text can be localized but must link to the same privacy policy link provided above
      7. If the privacy policy is updated, you must ask the user to agree to the above terms again
    </Accordion>

    Once the user has accepted the privacy policy, record their acceptance using the following API call:

    <CodeGroup>
      ```bash cURL theme={null}
      curl --request PUT \
          --url 'https://staging.crossmint.com/api/2025-06-09/users/userId:johnd-123/legal-documents' \
          --header 'X-API-KEY: <your-server-api-key>' \
          --header 'Content-Type: application/json' \
          --data '{
              "type": "crossmint-privacy-policy",
              "acceptedAt": "2025-10-05T14:48:00"
          }'
      ```

      ```javascript Node.js theme={null}
      const userLocator = "userId:johnd-123";

      const options = {
          method: 'PUT',
          headers: {'X-API-KEY': '<your-server-api-key>', 'Content-Type': 'application/json'},
          body: JSON.stringify({
              type: "crossmint-privacy-policy",
              acceptedAt: "2025-10-05T14:48:00" // ISO 8601 date string
          })
      };

      fetch(`https://staging.crossmint.com/api/2025-06-09/users/${userLocator}/legal-documents`, options)
          .then(response => response.json())
          .then(response => console.log(response))
          .catch(err => console.error(err));
      ```

      ```python Python theme={null}
      import requests

      user_locator = "userId:johnd-123"

      url = f"https://staging.crossmint.com/api/2025-06-09/users/{user_locator}/legal-documents"

      payload = {
          "type": "crossmint-privacy-policy",
          "acceptedAt": "2025-10-05T14:48:00"  # ISO 8601 date string
      }
      headers = {
          "X-API-KEY": "<your-server-api-key>",
          "Content-Type": "application/json"
      }

      response = requests.put(url, json=payload, headers=headers)

      print(response.json())
      ```
    </CodeGroup>

    <Note>This call also creates a user with the specified `userLocator` if one does not already exist.</Note>
  </Step>

  <Step title="Register user information">
    Register a user with Crossmint by specifying their `userLocator`, their personal details, and KYC data using the [Create User](/api-reference/users/upsert-user) endpoint

    <CodeGroup>
      ```bash cURL theme={null}
      curl --request PUT \
          --url 'https://staging.crossmint.com/api/2025-06-09/users/userId:johnd-123' \
          --header 'X-API-KEY: <your-server-api-key>' \
          --header 'Content-Type: application/json' \
          --data '{
              "userDetails": {
                  "firstName": "John",
                  "lastName": "Doe",
                  "dateOfBirth": "1995-01-01",
                  "countryOfResidence": "DE"
              }
          }'
      ```

      ```javascript Node.js theme={null}
      const userLocator = "userId:johnd-123"; 

      const options = {
          method: 'PUT',
          headers: {'X-API-KEY': '<your-server-api-key>', 'Content-Type': 'application/json'},
          body: JSON.stringify({
              userDetails: {
                  firstName: "John",
                  lastName: "Doe",
                  dateOfBirth: "1995-01-01",
                  countryOfResidence: "DE"
              }
          })
      };

      fetch(`https://staging.crossmint.com/api/2025-06-09/users/${userLocator}`, options)
          .then(response => response.json())
          .then(response => console.log(response))
          .catch(err => console.error(err));
      ```

      ```python Python theme={null}
      import requests

      user_locator = "userId:johnd-123"

      url = f"https://staging.crossmint.com/api/2025-06-09/users/{user_locator}"

      payload = {
          "userDetails": {
              "firstName": "John",
              "lastName": "Doe",
              "dateOfBirth": "1995-01-01",
              "countryOfResidence": "DE"
          }
      }
      headers = {
          "X-API-KEY": "<your-server-api-key>",
          "Content-Type": "application/json"
      }

      response = requests.put(url, json=payload, headers=headers)

      print(response.json())
      ```
    </CodeGroup>
  </Step>

  <Step title="Run identity verification">
    Once you have registered relevant user information you can trigger checks via the [Trigger Identity Verification](/api-reference/users/trigger-identity-verification) endpoint.

    <CodeGroup>
      ```bash cURL theme={null}
      curl --request PUT \
          --url 'https://staging.crossmint.com/api/2025-06-09/users/userId:johnd-123/identity-verification' \
          --header 'X-API-KEY: <your-server-api-key>'
      ```

      ```javascript Node.js theme={null}
      const userLocator = "userId:johnd-123";

      const options = {
          method: 'PUT',
          headers: {
              'X-API-KEY': '<your-server-api-key>'
          }
      };

      fetch(`https://staging.crossmint.com/api/2025-06-09/users/${userLocator}/identity-verification`, options)
          .then(response => response.json())
          .then(response => console.log(response))
          .catch(err => console.error(err));
      ```

      ```python Python theme={null}
      import requests

      user_locator = "userId:johnd-123"

      url = f"https://staging.crossmint.com/api/2025-06-09/users/{user_locator}/identity-verification"

      headers = {
          "X-API-KEY": "<your-server-api-key>"
      }

      response = requests.put(url, headers=headers)

      print(response.json())
      ```
    </CodeGroup>

    The response will include the eligibility status for each verification type (`regulated-transfer`, `onramp`, `offramp`):

    * `not-started`: Verification has not been initiated
    * `pending-privacy-policy`: User has not yet accepted Crossmint's Privacy Policy
    * `requires-data`: Additional user data or documents are needed
    * `pending-review`: Verification is in progress
    * `verified`: User has passed verification
    * `rejected`: User has failed verification

    If any of them return `requires-data`, you must aggregate the `missingData` and `missingDocuments` arrays from those specific eligibility objects. Then, update the user's information using the [Update User](/api-reference/users/upsert-user) endpoint and/or upload additional documents using the [Upload Document](/api-reference/users/upload-document) endpoint, and finally re-trigger the verification.

    ```json theme={null}
    {
        "eligibility": [
            {
                "type": "regulated-transfer",
                "status": "pending-review" // or "verified"
            },
            {
                "type": "onramp",
                "status": "requires-data",
                "missingData": ["kyc-data", "due-diligence", "verification-history"],
                "missingDocuments": ["id"] // "proof-of-address" and "proof-of-income" requested in case of additional due diligence required 
            },
            {
                "type": "offramp",
                "status": "requires-data",
                "missingData": ["kyc-data", "due-diligence", "verification-history"],
                "missingDocuments": ["id"] // "proof-of-address" and "proof-of-income" requested in case of additional due diligence required 
            }
        ]
    }
    ```
  </Step>

  <Step title="Update user information">
    Register additional user information with Crossmint so the user can access onramp and offramp services.

    <Note> Calling this endpoint again with the same `userLocator` will update the existing user's information. The endpoint is not additive so specify all relevant user information when updating the user.</Note>

    <CodeGroup>
      ```bash cURL theme={null}
      curl --request PUT \
          --url 'https://staging.crossmint.com/api/2025-06-09/users/userId:johnd-123' \
          --header 'X-API-KEY: <your-server-api-key>' \
          --header 'Content-Type: application/json' \
          --data '{
              "userDetails": {
                  "firstName": "John",
                  "lastName": "Doe",
                  "dateOfBirth": "1995-01-01",
                  "countryOfResidence": "DE"
              },
              "kycData": {
                  "addressOfResidence": {
                      "line1": "123 Hauptstrasse",
                      "line2": "Apt 5",
                      "city": "Berlin",
                      "stateOrRegion": "Brandenburg",
                      "postalCode": "10115"
                  },
                  "email": "johnd@example.com",
                  "identityDocument": {
                      "type": "passport",
                      "number": "AS12321",
                      "issuingCountryCode": "GR"
                  }
              },
              "dueDiligence": {
                  "employmentStatus": "full-time",
                  "sourceOfFunds": "employment-income",
                  "industry": "finance-insurance"
              },
              "verificationHistory": {
                  "idVerificationTimestamp": "2024-01-15T10:30:00Z",
                  "livenessVerificationTimestamp": "2024-01-15T10:32:00Z"
              }
          }'
      ```

      ```javascript Node.js theme={null}
      const userLocator = "userId:johnd-123"; 

      const options = {
          method: 'PUT',
          headers: {'X-API-KEY': '<your-server-api-key>', 'Content-Type': 'application/json'},
          body: JSON.stringify({
              userDetails: {
                  firstName: "John",
                  lastName: "Doe",
                  dateOfBirth: "1995-01-01",
                  countryOfResidence: "DE"
              },
              kycData: {
                  addressOfResidence: {
                      line1: "123 Hauptstrasse",
                      line2: "Apt 5",
                      city: "Berlin",
                      stateOrRegion: "Brandenburg",
                      postalCode: "10115"
                  },
                  email: "johnd@example.com",
                  identityDocument: {
                      type: "passport",
                      number: "AS12321",
                      issuingCountryCode: "GR"
                  }
              },
              dueDiligence: {
                  employmentStatus: "full-time",
                  sourceOfFunds: "employment-income",
                  industry: "finance-insurance"
              },
              verificationHistory: {
                  idVerificationTimestamp: "2024-01-15T10:30:00Z",
                  livenessVerificationTimestamp: "2024-01-15T10:32:00Z"
              }
          })
      };

      fetch(`https://staging.crossmint.com/api/2025-06-09/users/${userLocator}`, options)
          .then(response => response.json())
          .then(response => console.log(response))
          .catch(err => console.error(err));
      ```

      ```python Python theme={null}
      import requests

      user_locator = "userId:johnd-123"

      url = f"https://staging.crossmint.com/api/2025-06-09/users/{user_locator}"

      payload = {
          "userDetails": {
              "firstName": "John",
              "lastName": "Doe",
              "dateOfBirth": "1995-01-01",
              "countryOfResidence": "DE"
          },
          "kycData": {
              "addressOfResidence": {
                  "line1": "123 Hauptstrasse",
                  "line2": "Apt 5",
                  "city": "Berlin",
                  "stateOrRegion": "Brandenburg",
                  "postalCode": "10115"
              },
              "email": "johnd@example.com",
              "identityDocument": {
                  "type": "passport",
                  "number": "AS12321",
                  "issuingCountryCode": "GR"
              }
          },
          "dueDiligence": {
              "employmentStatus": "full-time",
              "sourceOfFunds": "employment-income",
              "industry": "finance-insurance"
          },
          "verificationHistory": {
              "idVerificationTimestamp": "2024-01-15T10:30:00Z",
              "livenessVerificationTimestamp": "2024-01-15T10:32:00Z"
          }
      }
      headers = {
          "X-API-KEY": "<your-server-api-key>",
          "Content-Type": "application/json"
      }

      response = requests.put(url, json=payload, headers=headers)

      print(response.json())
      ```
    </CodeGroup>
  </Step>

  <Step title="Upload a document">
    Upload identity or supporting documents for the user using the [Upload Document](/api-reference/users/upload-document) endpoint. Documents are associated with the user via their `userLocator`.

    <CodeGroup>
      ```bash cURL theme={null}
      curl --request POST \
          --url 'https://staging.crossmint.com/api/2025-06-09/documents' \
          --header 'X-API-KEY: <your-server-api-key>' \
          --header 'Content-Type: application/json' \
          --data '{
              "reference": {
                  "userLocator": "userId:johnd-123"
              },
              "documentType": "id-passport",
              "data": "<base64-encoded-image>",
              "expiresAt": "2030-12-31"
          }'
      ```

      ```javascript Node.js theme={null}
      const options = {
          method: 'POST',
          headers: {'X-API-KEY': '<your-server-api-key>', 'Content-Type': 'application/json'},
          body: JSON.stringify({
              reference: {
                  userLocator: "userId:johnd-123"
              },
              documentType: "id-passport",
              data: "<base64-encoded-image>",
              expiresAt: "2030-12-31"
          })
      };

      fetch('https://staging.crossmint.com/api/2025-06-09/documents', options)
          .then(response => response.json())
          .then(response => console.log(response))
          .catch(err => console.error(err));
      ```

      ```python Python theme={null}
      import requests

      url = "https://staging.crossmint.com/api/2025-06-09/documents"

      payload = {
          "reference": {
              "userLocator": "userId:johnd-123"
          },
          "documentType": "id-passport",
          "data": "<base64-encoded-image>",
          "expiresAt": "2030-12-31"
      }
      headers = {
          "X-API-KEY": "<your-server-api-key>",
          "Content-Type": "application/json"
      }

      response = requests.post(url, json=payload, headers=headers)

      print(response.json())
      ```
    </CodeGroup>

    **Supported document types:**

    * Identity documents: `id-ssn`, `id-passport`, `id-idcard-front`, `id-idcard-back`
    * Supporting documents: `proof-of-address`, `proof-of-income`

    <Note>Calling this endpoint again with the same `userLocator` and `documentType` will update the existing document's registered information.</Note>
  </Step>

  <Step title="Run identity verification">
    Once you have registered the user's information and uploaded the required documents, trigger the KYC verification process using the [Trigger Identity Verification](/api-reference/users/trigger-identity-verification) endpoint.

    <CodeGroup>
      ```bash cURL theme={null}
      curl --request PUT \
          --url 'https://staging.crossmint.com/api/2025-06-09/users/userId:johnd-123/identity-verification' \
          --header 'X-API-KEY: <your-server-api-key>'
      ```

      ```javascript Node.js theme={null}
      const userLocator = "userId:johnd-123";

      const options = {
          method: 'PUT',
          headers: {
              'X-API-KEY': '<your-server-api-key>'
          }
      };

      fetch(`https://staging.crossmint.com/api/2025-06-09/users/${userLocator}/identity-verification`, options)
          .then(response => response.json())
          .then(response => console.log(response))
          .catch(err => console.error(err));
      ```

      ```python Python theme={null}
      import requests

      user_locator = "userId:johnd-123"

      url = f"https://staging.crossmint.com/api/2025-06-09/users/{user_locator}/identity-verification"

      headers = {
          "X-API-KEY": "<your-server-api-key>"
      }

      response = requests.put(url, headers=headers)

      print(response.json())
      ```
    </CodeGroup>

    The response will include the eligibility status for each verification type (`regulated-transfer`, `onramp`, `offramp`):

    ```json theme={null}
    {
        "eligibility": [
            {
                "type": "regulated-transfer",
                "status": "verified"
            },
            {
                "type": "onramp",
                "status": "pending-review"
            },
            {
                "type": "offramp",
                "status": "pending-review"
            }
        ]
    }
    ```
  </Step>

  <Step title="Check verification status">
    After triggering the identity verification, the process usually completes within a few seconds. You can check the current status using the [Get Identity Verification Status](/api-reference/users/get-identity-verification) endpoint.

    <CodeGroup>
      ```bash cURL theme={null}
      curl --request GET \
          --url 'https://staging.crossmint.com/api/2025-06-09/users/userId:johnd-123/identity-verification' \
          --header 'X-API-KEY: <your-server-api-key>'
      ```

      ```javascript Node.js theme={null}
      const userLocator = "userId:johnd-123";

      const options = {
          method: 'GET',
          headers: {
              'X-API-KEY': '<your-server-api-key>'
          }
      };

      fetch(`https://staging.crossmint.com/api/2025-06-09/users/${userLocator}/identity-verification`, options)
          .then(response => response.json())
          .then(response => console.log(response))
          .catch(err => console.error(err));
      ```

      ```python Python theme={null}
      import requests

      user_locator = "userId:johnd-123"

      url = f"https://staging.crossmint.com/api/2025-06-09/users/{user_locator}/identity-verification"

      headers = {
          "X-API-KEY": "<your-server-api-key>"
      }

      response = requests.get(url, headers=headers)

      print(response.json())
      ```
    </CodeGroup>
  </Step>

  <Step title="Fetch user information">
    You can fetch a user's information at any time to see their current status, including when they last accepted valid legal documents. Use the Get User endpoint to retrieve this information:

    <CodeGroup>
      ```bash cURL theme={null}
      curl --request GET \
          --url 'https://staging.crossmint.com/api/2025-06-09/users/userId:johnd-123' \
          --header 'X-API-KEY: <your-server-api-key>'
      ```

      ```javascript Node.js theme={null}
      const userLocator = "userId:johnd-123";

      const options = {
          method: 'GET',
          headers: {
              'X-API-KEY': '<your-server-api-key>'
          }
      };

      fetch(`https://staging.crossmint.com/api/2025-06-09/users/${userLocator}`, options)
          .then(response => response.json())
          .then(response => console.log(response))
          .catch(err => console.error(err));
      ```

      ```python Python theme={null}
      import requests

      user_locator = "userId:johnd-123"

      url = f"https://staging.crossmint.com/api/2025-06-09/users/{user_locator}"

      headers = {
          "X-API-KEY": "<your-server-api-key>"
      }

      response = requests.get(url, headers=headers)

      print(response.json())
      ```
    </CodeGroup>

    The response includes the `legalDocuments` array, which shows the status of legal document acceptances:

    ```json theme={null}
    {
        "email": "john.doe@example.com",
        "phoneNumber": "+1234567890",
        "userId": "usr_1234567890",
        "userDetails": true,
        "kycData": false,
        "dueDiligence": false,
        "verificationHistory": false,
        "legalDocuments": [
            {
                "type": "crossmint-privacy-policy",
                "acceptedAt": "2024-01-15T10:30:00Z",
                "validVersion": true
            }
        ]
    }
    ```

    The `validVersion` field indicates whether the user has accepted the current version of the legal document. If `validVersion` is `false`, you should prompt the user to accept the updated terms.
  </Step>
</Steps>

## Launching in Production

For production, the steps are almost identical, but some changes are required:

1. Create a developer account on the [production console](https://www.crossmint.com/console)
2. Create a production server API key on the [API Keys](https://www.crossmint.com/console/projects/apiKeys) page with the API scopes `users.create`, `users.read`
3. Replace your test API key with the production key
4. Replace `staging.crossmint.com` with `www.crossmint.com` in the API URLs

## Learn More

<CardGroup cols={2}>
  <Card title="Data Requirements" icon="table" iconType="duotone" color="#6554C0" href="/identity/data-requirements">
    See what data is required for each activity and region.
  </Card>

  <Card title="Talk to an expert" icon="message" iconType="duotone" color="#ADD8E6" href="https://www.crossmint.com/contact/sales" target="_blank" rel="noopener">
    Contact the Crossmint sales team for support.
  </Card>
</CardGroup>
