# Quickstart

> Part of the Cowliss docs. Markdown for every page: append `index.md` to its URL. Site map: /llms.txt

Cowliss holds the people who use your product and mails them when something happens. Five things:

1. **`identify`** your users.
2. **`track`** what they do.
3. **Send** a transactional email from your own code.
4. **Segment** them.
5. **Write an automation**: a JSON definition, kept in your own repo.

## 1. Create an account and an API key

Sign up on the Cowliss dashboard, then invite your team from **Settings**.

Under **Settings → Credentials**, create an API key. The secret is shown once, so copy it now. One key serves all your apps.

## 2. Register an app

Every user and event carries the app it came from, so register one first: **Apps → New app**, or from the CLI:

```sh
cow login                       # browser flow, once
cow apps create --name "my-app" # prints app_my-app
cow apps sources list app_my-app
```

You end up with two ids. `appId` (`app_my-app`) is the product itself: your profiles, segments and automations all belong to it, and it is what `cow.json` and every list filter name. `sourceId` (`src_…`) is what you send on every call. The second command reads it back.

### Check the id before you send to it

Your key is scoped to the organization, not to one app, so **any** live `src_` in it is accepted: send to another app's id and the write succeeds with a 200 and the profiles land there. One look costs nothing:

```sh
cow whoami --key ak_… --source src_…
```

```json
{
  "data": {
    "orgId": "org_2b…",
    "app": { "id": "app_my-app", "name": "my-app" },
    "sources": [
      { "id": "src_01j2x8…", "kind": "api", "configured": true, "lastReceivedAt": null }
    ]
  }
}
```

That is the app the pair writes into, and every pipe already feeding it. `cow apps sources list <appId>` answers the same question from a login instead of a key.

Already have your users in Clerk? [Connect it as a second source](/tracking/apps/#connecting-clerk) and they arrive with no integration code, along with a signup and a login event per user.

## 3. Send identify and track

```sh
npm install @cowliss/sdk
```

```ts
import { Cow } from "@cowliss/sdk";

const cow = new Cow({
  apiKey: process.env.COW_KEY!,
  sourceId: process.env.COW_SOURCE_ID!,
});

await cow.identify({
  identifiers: { userId: "user_1" },
  traits: { email: "ada@acme-demo.io", plan: "free" },
});

await cow.track({
  identifiers: { userId: "user_1" },
  event: "checkout_started",
});
```

The response carries the `appId` the profile landed in. Since your `sourceId` comes from the environment, assert it once at boot: it is the cheap guard against a valid id pointing somewhere you did not mean.

`identifiers` names who the call is about (`userId`, `anonymousId`, `email`, `clerkId`). Cowliss owns the profile id and resolves your map to it. There is no alias call: naming two identifiers in one call links them. See [Identity](/tracking/identity/). Every field and client option is in the [SDK reference](/sdk/).

## 4. See the profile

Writes are synchronous, so the profile is already in **Users**, with `checkout_started` on its timeline. Or from the CLI, by any identifier:

```sh
cow users find --identifier userId:user_1
cow users events usr_…
```

## 5. Send one transactional email

A receipt, a password reset, an export that is ready. Your code decides when, so there is no automation and no rule.

```ts
await cow.emails.send({
  to: "ada@acme-demo.io",
  from: "Acme <hello@acme.com>",
  subject: "Your receipt",
  html: "<p>Thanks for your order.</p>",
});
```

Already sending with Resend? Point its SDK at Cowliss instead, and the code you have keeps working:

```ts
import { Resend } from "resend";

// RESEND_BASE_URL=https://api.cowliss.com/resend/app_app-cowliss-com
// RESEND_API_KEY=<your Cowliss API key>
const resend = new Resend();

await resend.emails.send({
  from: "Acme <hello@acme.com>",
  to: ["ada@acme-demo.io"],
  subject: "Your receipt",
  html: "<p>Thanks for your order.</p>",
});
```

Either way the message lands in the same delivery log as everything an automation sends. Both can send a [template](/emails/) you pushed instead of an inline body. See the [Resend SDK](/resend/) section.

## 6. Group people with a segment

A segment is a rule over traits and events, re-evaluated every time somebody is written.

Free-plan users who started a checkout this week:

```sh
cow segments create --name "checkout-stalled" --app-id app_my-app --definition '{
  "match": "all",
  "predicates": [
    { "kind": "trait", "name": "plan", "op": "eq", "value": "free" },
    { "kind": "event", "name": "checkout_started", "op": "performed", "withinDays": 7 }
  ]
}'
```

**Users → Segments → New segment** builds the same thing with a live count. Segments filter your users list, export to your BI tool, and can start an automation the moment somebody enters. See [Segments](/segments/).

## 7. Write an automation and turn it on

An automation is a JSON file in **your** repo that runs once per person: wait, check, send. (An API call can write the same definition, for a builder or an agent with no repo.)

```sh
npm install --save-dev @cowliss/cli
npx cow init && cd .cowliss && npm install
```

`cow init` writes the project into `.cowliss/` in your repo, and puts `app_my-app` in its `cow.json`, since it is your only app. Everything this repo pushes belongs to it, so no file below names it again. It also writes an example, `automations/welcome.json` with its emails and scenario, sending from your verified domain so it pushes as written. When `cow init` cannot read one (you are not logged in, or no domain is verified yet), its `from` says `example.com`, and a push refuses it until you put an address on your verified domain there or delete the example files.

Two files and one line in `cow.json`. An email says why it is sent. Your app declares its reasons in `.cowliss/cow.json`, each with the label a recipient reads:

```json title=".cowliss/cow.json"
"purposes": [{ "key": "marketing.cartReminders", "label": "Cart reminders" }]
```

Add it beside the `orgId` and `appId` already there. Now the email, which names that purpose. `{{firstName|there}}` reads the person's `firstName` trait:

```md title="emails/abandoned-checkout.md"
---
subject: Still thinking it over?
purpose: marketing.cartReminders
---

Hi {{firstName|there}}, your cart is still here.
```

An email can also be Liquid HTML, `emails/<key>.html`, when it needs your own layout, loops or conditions. See [Email templates](/emails/).

And the automation, triggered by the event you sent in step 3. It gives them a day to come back on their own, and stops if they buy:

```json title="automations/abandoned-checkout.json"
{
  "trigger": { "event": "checkout_started" },
  "purpose": "marketing",
  "from": "Acme <hello@acme.com>",
  "steps": [
    { "id": "wait", "type": "waitForEvent", "event": "purchase_completed", "timeout": "24h" },
    {
      "id": "purchased",
      "type": "branch",
      "if": { "var": "steps.wait.matched" },
      "then": [{ "id": "done", "type": "exit" }]
    },
    { "id": "remind", "type": "sendEmail", "template": "abandoned-checkout" }
  ]
}
```

The wait is durable: it survives deploys, and a purchase at hour 23 still cancels the email.

```sh
npx cow plan      # check the files and show what a push would change
npx cow push      # files a new version of whatever changed
npx cow enable abandoned-checkout
```

A pushed automation is off until you enable it. **Automations** then shows its steps, who is in flight and on which step, and every email that went out.

A marketing email reaches only people who said yes to its purpose. Once the push has declared it, record the yes on the `identify` call you already make: `consent: { "marketing.cartReminders": true }`. Without it the email is skipped, and the delivery says why. See [Consent](/sdk/identify/#consent).

## Where next

- [Your repo](/guides/repo/): the full loop, `cow init` to `cow pull`.
- [Automations](/automations/): triggers, the seven steps, rules, and four full examples.
- [Testing](/automations/testing/): replay an automation on a virtual clock, send nothing.
- [Connect your auth provider](/tracking/apps/#connecting-clerk): profiles, signups and logins arrive with no integration code.
- [Blueprints](/blueprints/): `cow add blueprint <name>`.
- [API reference](/reference/api/): generated from the schemas the server validates with.
