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

# Connect your app

> For developers: connect a website, SaaS or mobile backend to Hamla in about fifteen minutes, so the moments in your product start campaigns, fill audiences and count as revenue.

This is the one path from nothing to a working integration. It is written for the developer who wires it up. At the end, a real signup in your product reaches Hamla, a real event lands on that person's timeline, and a campaign can start the moment it happens.

You need three things: access to your Hamla dashboard, your site's HTML, and the server code where signups and payments happen.

> [!NOTE]
>
> **Testing first?** There is no separate test key yet. Every key is live, so an event you send can start a real campaign. Build against a second business you create for testing, and switch the key when you go live.

> [!NOTE]
>
> **Building with Lovable, v0 or Bolt for someone without a Hamla account?** Start with the claimable install on [hamla.io/developers](https://hamla.io/en/developers), then come back here once the owner has claimed it, because no secret key is issued before that.

## How the pieces fit

| Piece | Where it runs | What it tells Hamla | Key |
| - | - | - | - |
| Script tag | your web pages | visits, sources, carts, clicks, popups | none, the business id in it is public |
| `identify` | your server (or the page) | **who** someone is, what to call them | secret key, from a server |
| `track` | your server | **what happened**: a signup, a payment, a booking | secret key, required |

The script tag sees the browser. Your server knows the facts. Send both and Hamla sees the whole person.

**1. Add the script tag**

Copy the snippet from **Settings → Website** and put it in every page, before `</head>`:

```html theme={null}
<script src="https://app.hamla.io/s/YOUR_BUSINESS_ID.js" async></script>
```

It records visits and where they came from, shows popups, and gives every browser a visitor id. A mobile app with no web pages can skip this step.

**2. Create a secret key**

**Settings → API keys** → create a **secret** key. It starts with `sk_live_`.

Store it as `HAMLA_API_KEY` in your server's environment. It can write customer data, so treat it like a database password: never put it in a web page, a mobile app, or an env var your framework ships to the browser (`NEXT_PUBLIC_…`, `VITE_…`).

On Node.js, install the SDK:

```bash theme={null}
npm install @gethamla/node
```

```ts theme={null}
import { Hamla } from '@gethamla/node';
export const hamla = new Hamla(); // reads HAMLA_API_KEY
```

Any other language works over plain HTTP. The SDK only adds queueing and retries.

**3. Identify people when they sign up**

This is the step integrations most often skip, and it decides whether Hamla can ever message the person.

Call `identify` from your server at signup, and again at login:

```ts theme={null}
await hamla.identifyNow({
  identity: { email: user.email, platformCustomerId: user.id },
  profile: { name: user.name },
  method: 'signup',
  context: {
    userAgent: request.headers.get('user-agent') ?? undefined,
    country: request.headers.get('x-vercel-ip-country') ?? undefined,
  },
});
```

```bash theme={null}
curl -X POST https://app.hamla.io/api/sdk/identify \
  -H "Authorization: Bearer $HAMLA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "identity": { "email": "omar@raqmi.co", "platformCustomerId": "user_812" },
    "profile": { "name": "Omar Al Saleh" },
    "method": "signup"
  }'
```

What it does:

* Creates the contact, or enriches the one that already exists. Names and location fill gaps and never overwrite.
* Records that **you** put this person in Hamla, which is what lets campaigns email them. By identifying someone, you confirm you have permission to market to them, as Hamla's terms require.
* Links their earlier anonymous visits, including the ad that brought them, when you pass the `visitorId` cookie or call it from the page with `hamla.identify({ email })`.

> [!WARNING]
>
> **`track` alone does not make someone reachable.** A contact that Hamla only ever learned about from an event has no permission on record, so no campaign will send to them, not even an email. Call `identify` first, at the moment they sign up. Hamla's own signup works in this order.

SMS and WhatsApp need more than this: each requires the person's own opt-in for that channel, through a Hamla popup or form, or confirmed by the business in Hamla. An email or phone number on its own is never permission.

**4. Send events where they happen**

In the code you already have, such as the payment webhook, the booking confirmation or the trial start, add one call:

```ts theme={null}
hamla.track({
  event: 'subscription_started',
  type: 'purchase',
  identity: { platformCustomerId: user.id },
  value: 29,
  currency: 'USD',
  idempotencyKey: invoice.id,
  traits: { plan: 'pro' },
});
```

* `event` is your own name for the moment, in any language. Triggers and segments match it exactly.
* `type` says what it **means** (`purchase`, `refund`, `booking`, `signup`, `trial`…), so a payment counts as revenue. See [the full list](https://docs.hamla.io/reference/events.md#what-an-event-means).
* `idempotencyKey` is your invoice or order id. A retried webhook is then counted once.
* `traits` is who they are **now** (their plan, their status). The event is what they did.

`track` and `identify` never throw and never block, so a marketing call cannot break a checkout. On serverless platforms, call `await hamla.flush()` before the function returns so the queue is sent.

**5. Check it arrived**

Use `trackNow` once, so you can read the answer:

```ts theme={null}
const result = await hamla.trackNow({ event: 'subscription_started', type: 'purchase', identity: { email: 'you@yourcompany.com' }, value: 29, currency: 'USD' });
console.log(result);
```

The result tells you whether it landed and what Hamla decided:

```json theme={null}
{
  "ok": true,
  "status": 200,
  "data": {
    "contactId": "cm4x...",
    "isNew": false,
    "deduped": false,
    "interpreted": { "type": "purchase", "countedRevenue": 29 }
  }
}
```

When `ok` is `false`, `reason` says why (`config`, `invalid_input`, `http`, `network` or `timeout`) and `status` carries the HTTP code. Over plain HTTP you get the same `data` inside `{ "success": true, "data": … }`.

Then open that person in **Contacts**: the event is on their timeline with the time it happened. `warnings` lists anything Hamla could not apply, such as a trait of the wrong type, and appears only when there is something to say. Read it on your first request. If it says **campaigns will not reach this contact**, the person was never identified: go back to step 3.

**6. Turn the event into a message**

Hand it to the business, or do it yourself:

* **Ask Hamla:** "When someone fires `subscription_started`, send them a welcome email with our three best tips."
* **Or build it:** open a campaign → **When does the campaign start?** → **App event** → **Your app's events** → type the event name.

From now on, everyone who fires that event and can be reached gets the message, the moment it happens.

## Bring your history

Send past signups and payments with `POST /api/sdk/track/batch`, up to 500 per request, each with its real `occurredAt` and an `idempotencyKey`. History fills profiles, revenue and audiences, and **never** sends a message. Details in the [events reference](https://docs.hamla.io/reference/events.md).

## Before you go live

* [ ] The secret key is only on your server.
* [ ] `identify` runs at signup and login, before or alongside the first `track`.
* [ ] Every event that involves money has a `type` and an `idempotencyKey`.
* [ ] Serverless functions call `await hamla.flush()` before returning.
* [ ] You switched from your test business's key to the real one.

## When something is off

| What you see | Usual cause |
| - | - |
| `401` | No `Authorization` header, or the key was revoked. The error says which. |
| `403` | A publishable key (`pk_live_…`) on a server endpoint, or a `businessId` that does not match the key. |
| `429` | More than 120 requests a minute for one business. Wait for the window in the response headers, or batch. |
| The event is on the timeline, but no campaign started | The person was never identified, so there is no permission to message them (the `track` response says so in `warnings`); the event name differs by a character; the event came through the batch endpoint; or they were already enrolled and **Can enter again later** is off. |
| Revenue shows 0 | The event had no `type: "purchase"` and a name Hamla does not recognise. Check `interpreted` in the response. |

Every field, limit and error is in the [events reference](https://docs.hamla.io/reference/events.md). The longer explanation of events, with examples for a clinic and a SaaS, is in [Hamla Events](https://docs.hamla.io/how-to/hamla-events.md).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.