---
title: "Authentication"
description: "Authenticate with a bearer API key per organization, scoped to test mode or live mode, and learn what the mode check refuses and why that protects your data."
canonical_url: "https://twominutebilling.com/help/developers/api-essentials/authentication"
---
# Authentication

> Authenticate with a bearer API key per organization, scoped to test mode or live mode, and learn what the mode check refuses and why that protects your data.

Send your API key as a bearer token on every request:

```bash
curl $TMBILLING_URL/v1/me \
  -H "Authorization: Bearer sk_live_…"
```

A key identifies one organization. Everything it reaches — businesses, customers, invoices —
belongs to that organization, and tmbilling enforces the boundary in the database rather than in
whichever query happens to run. You cannot read another organization's data by guessing an id.

## Select a key mode

Every business has a live mode and a test mode, each with its own business id. You create each key
for one of the two modes:

<table>
<thead>
  <tr>
    <th>
      Mode
    </th>
    
    <th>
      Prefix
    </th>
    
    <th>
      Reaches
    </th>
    
    <th>
      Use it for
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        test
      </code>
    </td>
    
    <td>
      <code>
        sk_test_
      </code>
    </td>
    
    <td>
      Test mode only
    </td>
    
    <td>
      Development, CI, anything that must never charge a card
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        live
      </code>
    </td>
    
    <td>
      <code>
        sk_live_
      </code>
    </td>
    
    <td>
      Live mode only
    </td>
    
    <td>
      Production
    </td>
  </tr>
</tbody>
</table>

Point a test key at a live business id, or the reverse, and you get `404 No such business`. That
refusal is what stops a staging deployment with the wrong key from billing your real customers.

Time Travel and configuration transfer cross the two modes, so they run from the dashboard, which
signs in with its own credential. An integration key gets `403` on them. See
[Test and live mode](https://twominutebilling.com/help/developers/getting-started/test-and-live).

## Create and revoke keys

Create keys in the dashboard under **Settings → Developer → API keys**, or through the API:

<endpoint method="POST" path="/v1/api_keys">



</endpoint>

Pass `livemode` (`test` or `live`, default `test`), an optional `name`, and an optional `scope`
(`write`, the default, or `read`). A `read` key cannot change anything. tmbilling returns the
secret exactly once, when you create the key. `GET /v1/api_keys` lists your
keys with a non-secret fragment so you can identify them, and never returns a secret again. If you
lose a key, revoke it and create another.

<endpoint method="POST" path="/v1/api_keys/{id}/revoke">



</endpoint>

Revocation takes effect immediately.

<warning title="Keep your key on your server">

An API key reads every customer and charges every card in your organization. Never put one in a
browser, a mobile app or a repository.

For buyer-facing flows, create a hosted checkout session or a customer portal session instead.
Each gives you a short-lived, single-use credential scoped to one buyer. See
[Hosted checkout and portal](https://twominutebilling.com/help/developers/recipes/checkout-and-portal).

</warning>

## Check which organization a key resolves to

<endpoint method="GET" path="/v1/me">



</endpoint>

Use this as a deployment smoke test. If it returns the wrong organization or the wrong mode, stop
before anything else runs.

## Recognise an authentication failure

<table>
<thead>
  <tr>
    <th>
      Status
    </th>
    
    <th>
      Error
    </th>
    
    <th>
      Cause
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        401
      </code>
    </td>
    
    <td>
      <code>
        authentication_error
      </code>
    </td>
    
    <td>
      The key is missing, malformed or revoked.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        403
      </code>
    </td>
    
    <td>
      <code>
        permission_error
      </code>
    </td>
    
    <td>
      The key is valid but may not do this, for example a <code>
        read
      </code>
      
       key on a write.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        403
      </code>
    </td>
    
    <td>
      <code>
        permission_error
      </code>
      
      , code <code>
        live_access_required
      </code>
    </td>
    
    <td>
      Hosted only: our team turned live mode off for the organization. Test mode and reads still work. Contact us to turn live mode on.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        403
      </code>
    </td>
    
    <td>
      <code>
        permission_error
      </code>
      
      , code <code>
        account_restricted
      </code>
    </td>
    
    <td>
      Hosted only: the organization is read-only. Its plan is unpaid (pay the open invoice), or it stayed above its plan's limit and our team made it read-only (change to a plan that covers its billing).
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        404
      </code>
    </td>
    
    <td>
      <code>
        invalid_request_error
      </code>
    </td>
    
    <td>
      The business id belongs to the other mode, or does not exist.
    </td>
  </tr>
</tbody>
</table>

See [Errors](https://twominutebilling.com/help/developers/api-essentials/errors).

## Related

- [Security: keys, roles and the audit log](https://twominutebilling.com/security)
- [Test mode and live mode](https://twominutebilling.com/help/developers/getting-started/test-and-live)


## Sitemap

See the full [sitemap](https://twominutebilling.com/sitemap.md) for all pages.
