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

# Authentication

> Exchange your API keys for a bearer token and authenticate every request.

Zerocash uses two credentials to identify and authenticate your integration, which you exchange
for a short-lived bearer token. That token authorises every other endpoint.

## 1. Understand the credentials

<CardGroup cols={2}>
  <Card title="Public key" icon="id-card">
    Identifies your application. It forms part of the authentication process but is safe to
    expose, including in client-side code.
  </Card>

  <Card title="API key" icon="lock">
    Your secret. It is used together with the public key to mint a bearer token. Never ship it in
    client-side code or commit it to a repository.
  </Card>
</CardGroup>

## 2. Obtain your keys

<Steps>
  <Step title="Log in to your dashboard">
    Go to [app.zerocash.tech](https://app.zerocash.tech) and sign in.
  </Step>

  <Step title="Open API Keys">
    In the sidebar, find **API Keys** under the **Developers** section.
  </Step>

  <Step title="Copy both keys">
    You will see your **public key** and your **API key** (for example
    `ZC_yyyyyyyyyyyyyyyyyyyyyyyy`).
  </Step>
</Steps>

<Warning>
  Store the API key in an environment variable or a secrets manager — not inline in your source.
  Anyone holding it can move money on your account.
</Warning>

## 3. Generate a bearer token

Call [Generate Bearer Token](/api-reference/endpoint/generate-bearer-token) with your API key in
the `api-key` header and your public key in the body.

<CodeGroup>
  ```bash cURL theme={null}
  curl --request POST \
    --url https://api.zerocash.tech/api/b2b/auth/generate-bearer-token \
    --header 'accept: application/json' \
    --header 'api-key: YOUR_API_KEY' \
    --header 'content-type: application/json' \
    --data '{"publicKey":"YOUR_PUBLIC_KEY"}'
  ```

  ```javascript Node.js theme={null}
  const res = await fetch(
    "https://api.zerocash.tech/api/b2b/auth/generate-bearer-token",
    {
      method: "POST",
      headers: {
        "api-key": process.env.ZEROCASH_API_KEY,
        "content-type": "application/json",
        accept: "application/json",
      },
      body: JSON.stringify({ publicKey: process.env.ZEROCASH_PUBLIC_KEY }),
    }
  );

  const { token, expiresAt } = await res.json();
  ```

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

  res = requests.post(
      "https://api.zerocash.tech/api/b2b/auth/generate-bearer-token",
      headers={
          "api-key": os.environ["ZEROCASH_API_KEY"],
          "content-type": "application/json",
          "accept": "application/json",
      },
      json={"publicKey": os.environ["ZEROCASH_PUBLIC_KEY"]},
  )

  data = res.json()
  token, expires_at = data["token"], data["expiresAt"]
  ```
</CodeGroup>

A successful call returns the token and its expiry:

```json Response theme={null}
{
  "success": true,
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.....",
  "expiresAt": 1752085041
}
```

`expiresAt` is a Unix timestamp in **seconds**.

## 4. Authenticate your requests

Send the token in the `Authorization` header on every other endpoint.

```bash theme={null}
curl --request GET \
  --url https://api.zerocash.tech/api/b2b/transactions \
  --header 'Authorization: Bearer YOUR_BEARER_TOKEN' \
  --header 'Content-Type: application/json'
```

## 5. Handle token expiry

<Warning>
  Bearer tokens are valid until the `expiresAt` timestamp returned when they were issued. Do not
  hard-code a fixed token lifetime — read `expiresAt` and refresh against it.
</Warning>

Your integration should:

1. Store the `expiresAt` value alongside the token.
2. Refresh the token *before* that timestamp is reached.
3. Re-call the Bearer Token endpoint to mint a replacement when the current token nears expiry.

A practical pattern is to cache the token in memory and treat it as stale a minute or two early,
so an in-flight request never lands with a just-expired token.

<Accordion title="Example: a self-refreshing token cache" icon="rotate">
  ```javascript theme={null}
  let cached = { token: null, expiresAt: 0 };

  // Refresh 60s early so in-flight requests never carry a just-expired token.
  const SKEW_SECONDS = 60;

  export async function getBearerToken() {
    const now = Math.floor(Date.now() / 1000);
    if (cached.token && now < cached.expiresAt - SKEW_SECONDS) {
      return cached.token;
    }

    const res = await fetch(
      "https://api.zerocash.tech/api/b2b/auth/generate-bearer-token",
      {
        method: "POST",
        headers: {
          "api-key": process.env.ZEROCASH_API_KEY,
          "content-type": "application/json",
        },
        body: JSON.stringify({ publicKey: process.env.ZEROCASH_PUBLIC_KEY }),
      }
    );

    if (!res.ok) throw new Error(`Token request failed: ${res.status}`);

    const { token, expiresAt } = await res.json();
    cached = { token, expiresAt };
    return token;
  }
  ```
</Accordion>

## Best practices

<CardGroup cols={2}>
  <Card title="Keep the API key server-side" icon="server">
    Mint tokens from your backend. Never call the token endpoint from a browser or mobile client
    where the API key would be exposed.
  </Card>

  <Card title="Refresh proactively" icon="rotate">
    Generate a new token before the current one expires so traffic is never interrupted by a
    `401`.
  </Card>

  <Card title="Centralise token management" icon="boxes-stacked">
    Put refresh logic in one place rather than in each call site, so expiry is handled
    consistently.
  </Card>

  <Card title="Handle auth failures explicitly" icon="triangle-exclamation">
    Treat `401` as "mint a new token and retry once", and surface repeated failures rather than
    looping.
  </Card>
</CardGroup>
