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

# Authentication

> Create SnapIt API keys and authenticate programmatic /api/v1 requests.

# Authentication

The public programmatic API uses **tenant API keys**, not your login JWT.

| Auth type | Where used | Header |
| - | - | - |
| API key | `/api/v1/*` (uploads, invoice JSON, balance, top-up) | `Authorization: Bearer sk_live_…` |
| Session JWT | Web app + most `/api/*` routes | `Authorization: Bearer <jwt>` |

This page covers **API keys**. Session login is described in [Getting started](/guides/quickstart).

## Create a key in the app

<Steps>
  <Step title="Open API Keys">
    Go to **Settings → API Keys**.
  </Step>

  <Step title="Create or rotate">
    Each workspace has **one active** key for programmatic uploads, balance checks, and top-up.
    Create a key, or **Rotate** to invalidate the old one.
  </Step>

  <Step title="Copy once">
    The full `sk_live_…` secret is shown **once**. Store it in your secrets manager; the UI only shows a masked prefix afterward.
  </Step>
</Steps>

<Warning>
  Rotating or revoking a key immediately breaks integrations still using the old secret. Update clients before or right after rotate.
</Warning>

Keys are also offered during **onboarding** after signup — save that reveal if you plan to integrate immediately.

## Request header

```http theme={null}
Authorization: Bearer sk_live_your_key_here
```

Include the `Bearer ` prefix. The Nest OpenAPI security scheme name is `api-key` (API key in the `Authorization` header), which Mintlify's playground reads from `openapi/programmatic.json`.

<CodeGroup>
  ```bash curl theme={null}
  curl https://api.trysnapit.com/api/v1/credits/balance \
    -H "Authorization: Bearer sk_live_…"
  ```

  ```javascript Node theme={null}
  const res = await fetch('https://api.trysnapit.com/api/v1/credits/balance', {
    headers: { Authorization: `Bearer ${process.env.SNAPIT_API_KEY}` },
  });
  const data = await res.json();
  ```

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

  r = requests.get(
      'https://api.trysnapit.com/api/v1/credits/balance',
      headers={'Authorization': f"Bearer {os.environ['SNAPIT_API_KEY']}"},
  )
  r.raise_for_status()
  print(r.json())
  ```
</CodeGroup>

## What the API key can do

| Capability | Endpoints |
| - | - |
| Check balance | `GET /api/v1/credits/balance` |
| Top up credits | `GET /api/v1/credits/packages` → `POST /api/v1/credits/checkout` → pay → `POST /api/v1/credits/checkout/confirm` |
| Upload invoice | One-step `POST /api/v1/invoices/upload`, or init + attach |
| Get extracted JSON | `GET /api/v1/invoices/:id` (poll until `ready`) |

## Upload with an API key

| Flow | Endpoints |
| - | - |
| One-step | `POST /api/v1/invoices/upload` (multipart `file`) |
| Two-step | `POST /api/v1/invoices/upload/init` then `POST /api/v1/invoices/upload/{invoiceId}/file` |

Optional `vendorId` (query or multipart form field) steers Tier 1 template extraction. Responses are **202 Accepted** — extraction is async. The body includes `statusUrl` (relative path `/api/v1/invoices/{id}` — prepend the API host).

Poll until extraction finishes:

```bash theme={null}
curl https://api.trysnapit.com/api/v1/invoices/INVOICE_ID \
  -H "Authorization: Bearer sk_live_…"
```

| Field | Meaning |
| - | - |
| `ready` | `false` while queued/processing; `true` when finished |
| `processingStatus` | `queued`, `processing`, `completed`, `needs_review`, or `failed` |
| `data` | Structured invoice JSON (`invoice_number`, amounts, `line_items`, …). `null` until a result exists |

See [API overview](/api-reference/introduction) and the generated playground pages.

## Top up with an API key

```bash theme={null}
# 1. List packages
curl https://api.trysnapit.com/api/v1/credits/packages \
  -H "Authorization: Bearer sk_live_…"

# 2. Start checkout
curl -X POST https://api.trysnapit.com/api/v1/credits/checkout \
  -H "Authorization: Bearer sk_live_…" \
  -H "Content-Type: application/json" \
  -d '{"packageId":"PACKAGE_UUID"}'

# 3. Open the returned `url` in a browser, complete Stripe Checkout

# 4. Confirm (idempotent)
curl -X POST https://api.trysnapit.com/api/v1/credits/checkout/confirm \
  -H "Authorization: Bearer sk_live_…" \
  -H "Content-Type: application/json" \
  -d '{"sessionId":"cs_…"}'
```

You can also top up in the app: **Settings → Billing**. Details: [Credits and billing](/guides/credits).

## Errors

| Status | Meaning |
| - | - |
| `401` | Missing or invalid API key |
| `402` | Insufficient credits (on upload debit) |
| `400` | Unsupported file type, bad request, or file already attached |
| `404` | Invoice / package / checkout session not found |
| `503` | Extraction queue unavailable (credits refunded) or Stripe not configured |


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