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

# API Introduction

> Base URL, versioning, the response envelope, error codes, and rate limits for the PayPunch v1 REST API.

The PayPunch API is a versioned JSON REST API for building integrations against
your PayPunch data — bookkeeper organizations, pay periods, and employee
timesheets. This reference is generated from the real `/api/v1` route handlers.

<Note>
  This API is for **programmatic access**. The PayPunch web app and mobile
  clients use these same endpoints. Treat tokens and credentials as secrets.
</Note>

## Base URL

Every endpoint is mounted under `/api/v1` on your PayPunch deployment.

<CodeGroup>
  ```text Production (example) theme={null}
  https://app.paypunch.io/api/v1
  ```

  ```text Local development theme={null}
  http://localhost:3000/api/v1
  ```
</CodeGroup>

The host is whatever your deployment's `NEXT_PUBLIC_APP_URL` resolves to. All
paths in this reference are written relative to the `/api/v1` prefix
(for example, `POST /auth/login` is `POST {baseURL}/api/v1/auth/login`).

## Versioning

The version is encoded in the URL path (`/api/v1/...`). A breaking change ships
under a new prefix (`/api/v2`), so integrations pinned to `v1` keep working.

## Response envelope

Every endpoint returns the same JSON envelope:

```json theme={null}
{
  "success": true,
  "data": { },
  "error": "string (only on failure)",
  "message": "string (optional human-readable detail)"
}
```

<ResponseField name="success" type="boolean" required>
  `true` when the request succeeded, `false` otherwise. Always present.
</ResponseField>

<ResponseField name="data" type="object">
  The result payload. Present on success. Its shape is documented per endpoint.
</ResponseField>

<ResponseField name="error" type="string">
  A short error label (for example, `"Authentication required"`). Present on failure.
</ResponseField>

<ResponseField name="message" type="string">
  An optional, more detailed human-readable message. May appear on both success
  and failure responses.
</ResponseField>

### Authentication responses

Login and verification endpoints embed the JWT inside `data` alongside the
returned record:

```json theme={null}
{
  "success": true,
  "data": {
    "user": { },
    "token": "eyJhbGciOiJ..."
  }
}
```

### Paginated list responses

List endpoints (such as bookkeeper orgs and pay periods) wrap results in a
pagination object inside `data`:

```json theme={null}
{
  "success": true,
  "data": {
    "items": [],
    "total": 42,
    "page": 1,
    "pageSize": 20,
    "totalPages": 3
  }
}
```

## Error codes

Errors use standard HTTP status codes with the envelope above (`success: false`).

| Status | `error` example                                     | When it happens                                                                                 |
| ------ | --------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
| `400`  | `Invalid login data` / `Overlapping pay period`     | Validation failed (Zod), or a business rule was violated. `message` lists the offending fields. |
| `401`  | `Authentication failed` / `Authentication required` | Missing/invalid credentials, or a missing/expired token.                                        |
| `403`  | `Access denied` / `Account inactive`                | Authenticated but not allowed (wrong tenant, deactivated account/org/company).                  |
| `404`  | `Pay period not found`                              | The requested resource does not exist.                                                          |
| `429`  | `Too many login attempts`                           | Rate limit exceeded (login only — see below).                                                   |
| `500`  | `Login failed` / `Failed to fetch ...`              | Unexpected server error.                                                                        |

Validation errors come from [Zod](https://zod.dev) and return a `message` of the
form `field: reason, field: reason`.

## Rate limits

The login endpoint is rate limited by client IP: **5 attempts per 15 minutes**.
When exceeded it returns `429` with these headers:

| Header                  | Meaning                                    |
| ----------------------- | ------------------------------------------ |
| `X-RateLimit-Limit`     | Maximum attempts allowed in the window.    |
| `X-RateLimit-Remaining` | Attempts remaining in the current window.  |
| `X-RateLimit-Reset`     | ISO-8601 timestamp when the window resets. |

<Note>
  Rate limiting uses Upstash Redis when `UPSTASH_REDIS_REST_URL` /
  `UPSTASH_REDIS_REST_TOKEN` are configured, otherwise an in-memory limiter
  (per-instance). The login limit is the only limit enforced inside the
  documented `/api/v1` handlers; broader per-IP API/public limits exist in the
  codebase but are not applied by these specific routes.
</Note>

## Next steps

<CardGroup cols={2}>
  <Card title="Authentication" icon="key" href="/api-reference/authentication">
    How to obtain and send a JWT for admin and employee requests.
  </Card>

  <Card title="Admin: Bookkeeper Orgs" icon="building" href="/api-reference/admin-bookkeeper-orgs">
    List, create, read, update, and deactivate bookkeeper organizations.
  </Card>
</CardGroup>
