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

# Authentication

> Secure your API requests with Bearer tokens

## API Keys

All API requests require authentication using a Bearer token in the Authorization header.

```bash theme={null}
Authorization: Bearer zv_live_xxxxxxxxxxxxxxxxxxxxxxxx
```

## Key Types

| Prefix     | Environment | Usage                                 |
| ---------- | ----------- | ------------------------------------- |
| `zv_live_` | Production  | Real messages, real costs             |
| `zv_test_` | Sandbox     | Testing without sending real messages |

<Warning>
  Test keys (`zv_test_`) simulate message sending but don't actually deliver messages. Use them for development and testing.
</Warning>

## Creating API Keys

1. Log in to your [Zavu Dashboard](https://dashboard.zavu.dev)
2. Navigate to **Settings** → **API Keys**
3. Click **Create API Key**
4. Give it a descriptive name (e.g., "Production Server", "Development")
5. Copy and securely store the key

<Info>
  API keys are only shown once at creation. If you lose a key, you'll need to create a new one.
</Info>

## Using API Keys

### In HTTP Requests

Include the key in the `Authorization` header:

```bash theme={null}
curl https://api.zavu.dev/v1/messages \
  -H "Authorization: Bearer zv_live_xxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json"
```

### In SDKs

<CodeGroup>
  ```typescript TypeScript theme={null}
  import Zavudev from '@zavudev/sdk';

  const zavu = new Zavudev({
    apiKey: process.env['ZAVUDEV_API_KEY'], // This is the default and can be omitted
  });
  ```

  ```python Python theme={null}
  import os
  from zavudev import Zavudev

  zavu = Zavudev(
      api_key=os.environ.get("ZAVUDEV_API_KEY"),  # This is the default and can be omitted
  )
  ```

  ```ruby Ruby theme={null}
  require "zavudev"

  client = Zavudev::Client.new(api_key: ENV["ZAVUDEV_API_KEY"])
  ```

  ```go Go theme={null}
  import (
  	"os"
  	"github.com/zavudev/sdk-go"
  	"github.com/zavudev/sdk-go/option"
  )

  client := zavudev.NewClient(option.WithAPIKey(os.Getenv("ZAVUDEV_API_KEY")))
  ```

  ```php PHP theme={null}
  <?php
  $client = new Zavudev\Client(apiKey: getenv('ZAVUDEV_API_KEY'));
  ```
</CodeGroup>

## Optional Headers

| Header            | Description                 | Example                    |
| ----------------- | --------------------------- | -------------------------- |
| `Zavu-Sender`     | Override the default sender | `snd_abc123`               |
| `Idempotency-Key` | Prevent duplicate sends     | `order-12345-confirmation` |

### Zavu-Sender Header

Override the default sender for a specific request:

```bash theme={null}
curl -X POST https://api.zavu.dev/v1/messages \
  -H "Authorization: Bearer zv_live_xxx" \
  -H "Zavu-Sender: snd_abc123" \
  -H "Content-Type: application/json" \
  -d '{"to": "+56912345678", "text": "Hello!"}'
```

### Idempotency Keys

Prevent duplicate message sends due to network retries:

```bash theme={null}
curl -X POST https://api.zavu.dev/v1/messages \
  -H "Authorization: Bearer zv_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "+56912345678",
    "text": "Your order #12345 has shipped!",
    "idempotencyKey": "order-12345-shipped"
  }'
```

If you retry this request with the same `idempotencyKey`, you'll receive a `409 Conflict` with the original message instead of sending a duplicate.

## Security Best Practices

<Warning>
  Never expose your API keys in client-side code, public repositories, or browser applications.
</Warning>

### Do's

* Store keys in environment variables
* Use different keys for development and production
* Rotate keys periodically (every 90 days recommended)
* Use the minimum permissions needed
* Monitor key usage in your dashboard

### Don'ts

* Don't commit keys to version control
* Don't share keys via email or chat
* Don't use production keys in development
* Don't embed keys in mobile apps or frontends

## Frontend Integration

The Zavu API uses secret API keys that **must never be exposed in client-side code**. If you include your key in a browser app, anyone can open DevTools and steal it.

Instead, use the **Backend-for-Frontend (BFF) pattern**: your frontend calls your own server endpoint, and your server calls the Zavu API.

### Server-side proxy examples

<CodeGroup>
  ```typescript Next.js (App Router) theme={null}
  // app/api/send-message/route.ts
  import Zavudev from '@zavudev/sdk';

  const zavu = new Zavudev(); // reads ZAVUDEV_API_KEY from env

  export async function POST(request: Request) {
    const { to, text } = await request.json();

    const result = await zavu.messages.send({ to, text });
    return Response.json(result);
  }
  ```

  ```typescript Express.js theme={null}
  import express from 'express';
  import Zavudev from '@zavudev/sdk';

  const app = express();
  app.use(express.json());

  const zavu = new Zavudev(); // reads ZAVUDEV_API_KEY from env

  app.post('/api/send-message', async (req, res) => {
    const { to, text } = req.body;

    const result = await zavu.messages.send({ to, text });
    res.json(result);
  });
  ```

  ```python FastAPI theme={null}
  from fastapi import FastAPI
  from pydantic import BaseModel
  from zavudev import Zavudev

  app = FastAPI()
  zavu = Zavudev()  # reads ZAVUDEV_API_KEY from env

  class MessageRequest(BaseModel):
      to: str
      text: str

  @app.post("/api/send-message")
  async def send_message(req: MessageRequest):
      result = zavu.messages.send(to=req.to, text=req.text)
      return result
  ```

  ```ruby Ruby (Sinatra) theme={null}
  require "sinatra"
  require "sinatra/json"
  require "zavudev"

  client = Zavudev::Client.new # reads ZAVUDEV_API_KEY from env

  post "/api/send-message" do
    body = JSON.parse(request.body.read)
    result = client.messages.send(to: body["to"], text: body["text"])
    json result
  end
  ```

  ```go Go (net/http) theme={null}
  package main

  import (
  	"encoding/json"
  	"net/http"

  	"github.com/zavudev/sdk-go"
  	"github.com/zavudev/sdk-go/option"
  )

  var client = zavudev.NewClient() // reads ZAVUDEV_API_KEY from env

  func sendMessage(w http.ResponseWriter, r *http.Request) {
  	var req struct {
  		To   string `json:"to"`
  		Text string `json:"text"`
  	}
  	json.NewDecoder(r.Body).Decode(&req)

  	result, _ := client.Messages.Send(r.Context(), zavudev.MessageSendParams{
  		To:   zavudev.String(req.To),
  		Text: zavudev.String(req.Text),
  	})
  	json.NewEncoder(w).Encode(result)
  }

  func main() {
  	http.HandleFunc("/api/send-message", sendMessage)
  	http.ListenAndServe(":3000", nil)
  }
  ```

  ```php PHP (Laravel) theme={null}
  <?php
  use Illuminate\Http\Request;
  use Illuminate\Support\Facades\Route;

  $client = new Zavudev\Client(); // reads ZAVUDEV_API_KEY from env

  Route::post('/api/send-message', function (Request $request) use ($client) {
      $result = $client->messages->send([
          'to' => $request->input('to'),
          'text' => $request->input('text'),
      ]);
      return response()->json($result);
  });
  ```
</CodeGroup>

### Frontend example

```typescript React theme={null}
async function sendMessage(to: string, text: string) {
  const res = await fetch('/api/send-message', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ to, text }),
  });
  return res.json();
}
```

<Info>
  Add your own authentication and validation to the proxy endpoint. The examples above are simplified for clarity.
</Info>

## Key Permissions

API keys can be scoped to specific permissions:

| Permission        | Description                     |
| ----------------- | ------------------------------- |
| `*`               | Full access to all resources    |
| `messages:send`   | Send messages                   |
| `messages:read`   | Read message status and history |
| `templates:read`  | Read templates                  |
| `templates:write` | Create and update templates     |
| `contacts:read`   | Read contact information        |
| `contacts:write`  | Create and update contacts      |

## Revoking Keys

If a key is compromised:

1. Go to **Settings** → **API Keys**
2. Find the compromised key
3. Click **Revoke**
4. Create a new key
5. Update your applications

Revoked keys are immediately invalidated and cannot be restored.

## Error Responses

| Status | Error                 | Description                    |
| ------ | --------------------- | ------------------------------ |
| `401`  | `unauthorized`        | Missing or invalid API key     |
| `403`  | `forbidden`           | Key lacks required permissions |
| `429`  | `rate_limit_exceeded` | Too many requests              |

```json theme={null}
{
  "error": {
    "code": "unauthorized",
    "message": "Invalid API key"
  }
}
```
