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

# Quickstart

> Sign your first request in about ten minutes.

By the end of this page you will have made an authenticated call to the StableMint API. Start here
before writing any integration code — if your signing implementation is wrong, every other call
fails the same way, and it is much easier to debug against a `GET` with no body.

## What you need

<Steps>
  <Step title="Sandbox access">
    A sandbox partner account and a dashboard login. See [Sandbox](/guides/sandbox).
  </Step>

  <Step title="An RSA key pair">
    Generate one and upload the public half when you create a service account:

    ```bash theme={null}
    openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out stablemint-private.pem
    openssl rsa -in stablemint-private.pem -pubout -out stablemint-public.pem
    ```
  </Step>

  <Step title="A service account">
    Create one in the [Developer Hub](https://dashboard.stablemint.net/), register `stablemint-public.pem`, and note the API key and API
    secret. Grant it permission to read transactions so it can do something once it authenticates. See
    [Service accounts](/guides/service-accounts).
  </Step>
</Steps>

## Make the call

Set your credentials:

```bash theme={null}
export STABLEMINT_API_KEY="your-api-key"
export STABLEMINT_API_SECRET="your-api-secret"
export STABLEMINT_PRIVATE_KEY="$(cat stablemint-private.pem)"
```

Then sign and send a request for your accounts:

<CodeGroup>
  ```bash cURL theme={null}
  #!/usr/bin/env bash
  set -euo pipefail

  BASE_URL="https://api.stablemint.net"
  METHOD="GET"
  PATH_ONLY="/v1/accounts"

  TIMESTAMP=$(date +%s)
  NONCE=$(uuidgen | tr '[:upper:]' '[:lower:]')
  # SHA-256 of an empty body.
  BODY_HASH="e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"

  # Seven fields, LF-joined. printf gives us real newlines, not literal \n.
  CANONICAL=$(printf '%s\n%s\n%s\n%s\n%s\n%s\n%s' \
    "$METHOD" "$PATH_ONLY" "" "$TIMESTAMP" "$NONCE" "$STABLEMINT_API_SECRET" "$BODY_HASH")

  SIGNATURE=$(printf '%s' "$CANONICAL" \
    | openssl dgst -sha256 -sign <(printf '%s' "$STABLEMINT_PRIVATE_KEY") \
    | openssl base64 -A)

  curl -sS "${BASE_URL}${PATH_ONLY}" \
    -H "ApiKey: ${STABLEMINT_API_KEY}" \
    -H "Timestamp: ${TIMESTAMP}" \
    -H "Nonce: ${NONCE}" \
    -H "Signature: ${SIGNATURE}"
  ```

  ```javascript Node.js theme={null}
  import crypto from 'node:crypto';

  const BASE_URL = 'https://api.stablemint.net';
  const path = '/v1/accounts';

  const timestamp = Math.floor(Date.now() / 1000).toString();
  const nonce = crypto.randomUUID();
  const bodyHash = crypto.createHash('sha256').update('', 'utf8').digest('hex');

  const canonical = [
    'GET',
    path,
    '', // no query string
    timestamp,
    nonce,
    process.env.STABLEMINT_API_SECRET,
    bodyHash,
  ].join('\n');

  const signature = crypto
    .sign('sha256', Buffer.from(canonical, 'utf8'), {
      key: process.env.STABLEMINT_PRIVATE_KEY,
      padding: crypto.constants.RSA_PKCS1_PADDING,
    })
    .toString('base64');

  const response = await fetch(BASE_URL + path, {
    headers: {
      ApiKey: process.env.STABLEMINT_API_KEY,
      Timestamp: timestamp,
      Nonce: nonce,
      Signature: signature,
    },
  });

  console.log(response.status, await response.json());
  ```
</CodeGroup>

A `200` means your signing is correct and you can move on. Anything else is worth fixing now.

## If it did not work

<AccordionGroup>
  <Accordion title="401 signature_required">
    One of the four headers is missing or empty. Check that `STABLEMINT_API_KEY` is actually set —
    an unset variable expands to an empty header, which counts as missing.
  </Accordion>

  <Accordion title="401 jwt_not_accepted">
    Something added an `Authorization` header. The public API takes signed requests only.
  </Accordion>

  <Accordion title="401 with all four headers set">
    The signature did not verify. The two most common causes are a canonical string joined with
    literal `\n` characters instead of real line feeds, and a clock more than 60 seconds out of
    step. Check both before looking anywhere else.
  </Accordion>
</AccordionGroup>

## Next

<CardGroup cols={2}>
  <Card title="Authentication" icon="key" href="/guides/authentication">
    The full signing contract, including bodies and query strings.
  </Card>

  <Card title="Sandbox" icon="flask" href="/guides/sandbox">
    Simulate a deposit and watch it mint.
  </Card>
</CardGroup>
