# Developer API

Manage subscribers, track events, and send emails. Use the REST API directly or pick an [SDK](/docs/sdks).

<div className="not-prose mb-16 mt-6 flex gap-3">
  
    <>Get API Keys</>
  
  
    <>Explore SDKs</>
  
</div>

## Cheat Sheet {{ id: 'cheat-sheet' }}

**Base URL:** `https://app.bentonow.com/api/v1/`

| Endpoint | Method | What it does | Triggers Automations? |
|----------|--------|--------------|----------------------|
| `/batch/events` | POST | Track user activity, create users, update fields | Yes |
| `/batch/subscribers` | POST | Sync user data (like CSV import) | No |
| `/batch/emails` | POST | Send transactional emails | No |

**Required headers for all requests:**

```
Authorization: Basic {base64_encoded_credentials}
User-Agent: YourApp/1.0
Content-Type: application/json
```

---

## Before You Start {{ id: 'before-you-start' }}


> ⚠️ **Warning**
> **Common issues that will break your requests:**
> - **Missing User-Agent** — Cloudflare blocks requests without one
> - **Wrong auth format** — Base64 encode `publishable_key:secret_key` (with colon)
> - **Wrong site_uuid** — Each site has its own UUID, check your dashboard


**Shortcut:** Skip base64 encoding by putting credentials in the URL:

```bash
https://PUBLISHABLE_KEY:SECRET_KEY@app.bentonow.com/api/v1/...
```

---

## Authentication {{ id: 'authentication' }}

Use Basic auth with your `publishable_key` as username and `secret_key` as password. Add `site_uuid` as a query parameter.

**Generate base64 credentials:**

```bash
echo -n "your_publishable_key:your_secret_key" | base64
# Output: eW91cl9wdWJsaXNoYWJsZV9rZXk6eW91cl9zZWNyZXRfa2V5
```

**Example request:**

```bash
curl -X GET 'https://app.bentonow.com/api/v1/fetch/tags?site_uuid=YOUR_SITE_UUID' \
  -H 'Authorization: Basic YOUR_BASE64_CREDENTIALS' \
  -H 'User-Agent: MyApp/1.0'
```

Get your keys from [Settings → API Keys](https://app.bentonow.com/account/teams). Don't commit them to source control.

---

## Quick Examples {{ id: 'quick-examples' }}

### Track Events

Creates the event, creates the user if new, updates custom fields, and triggers automations.


```bash {{ title: 'cURL' }}
curl -X POST 'https://app.bentonow.com/api/v1/batch/events?site_uuid=YOUR_SITE_UUID' \
  -H 'Authorization: Basic YOUR_BASE64_CREDENTIALS' \
  -H 'User-Agent: MyApp/1.0' \
  -H 'Content-Type: application/json' \
  -d '{
    "events": [{
      "type": "account.signup",
      "email": "user@example.com",
      "fields": { "first_name": "Jesse" }
    }]
  }'
```

```js {{ title: 'Node SDK' }}
await bento.V1.track({
  email: 'user@example.com',
  type: 'account.signup',
  fields: { first_name: 'Jesse' }
})
```


<div className="not-prose mt-4">
  
    <>Full Events API docs</>
  
</div>

### Sync Subscribers

Bulk import/update users without triggering automations. Perfect for nightly syncs.


```bash {{ title: 'cURL' }}
curl -X POST 'https://app.bentonow.com/api/v1/batch/subscribers?site_uuid=YOUR_SITE_UUID' \
  -H 'Authorization: Basic YOUR_BASE64_CREDENTIALS' \
  -H 'User-Agent: MyApp/1.0' \
  -H 'Content-Type: application/json' \
  -d '{
    "subscribers": [{
      "email": "user@example.com",
      "first_name": "Jesse",
      "tags": "lead,mql",
      "remove_tags": "prospect"
    }]
  }'
```

```js {{ title: 'Node SDK' }}
await bento.V1.Batch.importSubscribers({
  subscribers: [{
    email: 'user@example.com',
    first_name: 'Jesse',
    tags: 'lead,mql'
  }]
})
```


<div className="not-prose mt-4">
  
    <>Full Subscribers API docs</>
  
</div>

### Send Transactional Emails

For time-sensitive emails like password resets. For marketing emails, use events + automations instead.


```bash {{ title: 'cURL' }}
curl -X POST 'https://app.bentonow.com/api/v1/batch/emails?site_uuid=YOUR_SITE_UUID' \
  -H 'Authorization: Basic YOUR_BASE64_CREDENTIALS' \
  -H 'User-Agent: MyApp/1.0' \
  -H 'Content-Type: application/json' \
  -d '{
    "emails": [{
      "to": "user@example.com",
      "from": "hello@yourapp.com",
      "subject": "Reset your password",
      "html_body": "<p>Click here to reset: {{ link }}</p>",
      "transactional": true
    }]
  }'
```

```js {{ title: 'Node SDK' }}
await bento.V1.Batch.sendTransactionalEmails({
  emails: [{
    to: 'user@example.com',
    from: 'hello@yourapp.com',
    subject: 'Reset your password',
    html_body: '<p>Click here to reset: {{ link }}</p>',
    transactional: true
  }]
})
```


<div className="not-prose mt-4">
  
    <>Full Emails API docs</>
  
</div>

### Client-Side Tracking

Add Bento.js to track page views and identify logged-in users:

```html
<script src="https://app.bentonow.com/{YOUR_SCRIPT}.js" async defer></script>
<script>
  window.addEventListener("bento:ready", function() {
    bento$(function() {
      // bento.identify('user@example.com'); // Call first if logged in
      bento.view();
    });
  });
</script>
```

<div className="not-prose mt-4">
  
    <>Full Bento.js docs</>
  
</div>

---

## Reference {{ id: 'reference' }}

### Rate Limits

| Endpoint | Limit |
|----------|-------|
| `/api/v1/fetch/*` | 100/min |
| `/api/v1/batch/*` | 100/min |
| `/api/v1/experimental/*` | 100/min |
| `/api/v1/stats/*` | 60/hour |

Need more? [Contact support](mailto:support@bentonow.com).

### Error Codes

| Code | Meaning | Fix |
|------|---------|-----|
| `400` | Bad request | Check request format |
| `401` | Unauthorized | Check keys and site_uuid |
| `429` | Rate limited | Slow down requests |
| `500` | Server error | Check [status page](https://status.bentonow.com) |
| `BLOCKED` | Cloudflare | Add User-Agent header |

---

## Need Help?

[Discord](https://discord.com/invite/ssXXFRmt5F) is fastest. Or email [support@bentonow.com](mailto:support@bentonow.com).