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

# Authentication

> Vouchmark uses a cookie-based session with a short-lived access token.

The Vouchmark API uses a **cookie-based session**. Logging in sets httpOnly cookies that carry your access and refresh tokens — the tokens are never returned in the JSON response body. Browsers send the cookies automatically; server integrations read the access token cookie and send it as a Bearer credential.

The same credentials power the dashboard at [vouchmark.com](https://vouchmark.com).

## Sign up and verify

`POST /v1/signup` creates an account and emails a six-digit verification code. No tokens are issued yet.

```json Response theme={null}
{
  "success": true,
  "message": "Verification code has been sent to your email",
  "data": {
    "userId": "usr_aBcD...",
    "requiresVerification": true
  }
}
```

The account cannot log in until the email is verified. If you call `POST /v1/login` before verifying, the API returns `403` with `data.requiresVerification: true` and re-sends the code:

```json Response theme={null}
{
  "success": false,
  "error": "Email not verified",
  "message": "Please verify your email before logging in. A new verification code has been sent to your email.",
  "data": {
    "requiresVerification": true,
    "userId": "usr_aBcD..."
  }
}
```

## Log in

`POST /v1/login` authenticates the user and, on success, sets the session cookies. The JSON body confirms success but contains no tokens.

<CodeGroup>
  ```bash cURL theme={null}
  curl -i -X POST https://api.vouchmark.com/v1/login \
    -H "Content-Type: application/json" \
    -d '{"email":"you@example.com","password":"••••••••"}'
  ```

  ```ts Node theme={null}
  const res = await fetch("https://api.vouchmark.com/v1/login", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ email, password }),
  });
  const setCookie = res.headers.get("set-cookie");
  ```
</CodeGroup>

```json Response theme={null}
{
  "success": true,
  "message": "Login Successful",
  "data": {
    "userId": "usr_aBcD..."
  }
}
```

Login sets three cookies:

| Cookie         | httpOnly | Holds                                                           |
| -------------- | -------- | --------------------------------------------------------------- |
| `token`        | yes      | The access token (JWT).                                         |
| `refreshToken` | yes      | The refresh token, rotated on every refresh.                    |
| `vm_session`   | no       | A non-sensitive hint so the frontend can tell a session exists. |

## Call the API

For browser clients, the `token` cookie is sent automatically. For server-to-server use, read the value of the `token` cookie and pass it as a Bearer credential on every request:

```bash theme={null}
curl https://api.vouchmark.com/v1/dashboard/my-companies \
  -H "Authorization: Bearer <token-cookie-value>"
```

## Refresh the session

The access token is short-lived (**15 minutes**). The refresh token lasts **30 days**. When the access token expires, call `POST /v1/refresh-token`. The refresh token is read from the `refreshToken` cookie, or you can send it in the body. A new access token and a rotated refresh token are set as cookies — they are not returned in the body.

```json Response theme={null}
{
  "success": true,
  "message": "Token refreshed successfully"
}
```

<Warning>
  The refresh token is rotated on every call. The previous token is honoured only inside a short grace window to absorb concurrent requests; after that it is invalid. Treat the refresh token like a password.
</Warning>

## Sign out

`POST /v1/logout` invalidates the refresh session, blacklists the current access token in Redis for the rest of its lifetime, and clears all auth cookies.

```json Response theme={null}
{
  "success": true,
  "message": "Logged out successfully"
}
```

## Sandbox vs. production

Widget integrations use two **widget environments** — `sandbox` and `live` — each with its own publishable key prefix (`test_pk_` vs `live_pk_`) and widget IDs. See [SDK environments](/sdks/overview#environments) for how `environment`, keys, and widget IDs must align.

All API requests use `https://api.vouchmark.com/v1` — the same base URL for staging and production. Sandbox vs live is determined by your credentials, not by a different hostname.
