Pharos
API reference.md

Automations

Reading drip sequences and their runs. Authoring and editing automations is a dashboard act — see Automations for the concepts, step shape and worked examples.

The asymmetry is deliberate. A multi-step sequence that mails people on a schedule is exactly the kind of thing that should be built where a human can see the whole shape of it, so the API surface reads and the dashboard writes.

GET /api/automations

Every automation in the project.

Scope: automations:read

Parameter In Type Notes
projectId query string Required.
{
  "automations": [
    {
      "id": "aut_…",
      "name": "Welcome drip",
      "trigger": "tag_added",
      "triggerConfig": "{\"tag\":\"signed-up\"}",
      "enabled": true,
      "stepCount": 3,
      "runCount": 1204,
      "createdAt": "2026-06-12T08:00:00.000Z"
    }
  ]
}

triggerConfig is a JSON string, not an object — parse it before reading the tag.

GET /api/automations/{id}

One automation in full: its steps in order, run counts by status, and the ten most recent runs with any errors.

Scope: automations:read

{
  "automation": {
    "id": "aut_…",
    "name": "Welcome drip",
    "trigger": "tag_added",
    "enabled": true,
    "createdAt": "2026-06-12T08:00:00.000Z"
  },
  "steps": [
    { "id": "stp_…", "position": 1, "delayHours": 0, "subject": "Welcome" }
  ],
  "runs": {
    "counts": { "pending": 12, "completed": 1180, "failed": 3 },
    "recent": [
      {
        "id": "run_…",
        "contactEmail": "reader@example.com",
        "status": "completed",
        "scheduledFor": "2026-07-30T09:00:00.000Z",
        "executedAt": "2026-07-30T09:00:11.000Z",
        "error": null
      }
    ]
  }
}

runs.counts is keyed by status across the automation's whole history; runs.recent is the last ten. A run carrying an error is the first place to look when a sequence stops mid-way.

Triggering an automation

Automations fire on tags, so your application starts one by tagging a contact rather than by naming a sequence. That indirection is the point: your app knows the user did something, and the sequence attached to it can change without redeploying the app.

Two ways to add the tag.

PATCH /api/contacts/{id} — the API-key path

Scope: contacts:write. Tags are replaced wholesale, so read the current list first and add to it. See Contacts.

This is the right choice for anything new.

POST /api/contacts/event — the legacy webhook path

Predates project API keys and authenticates differently: a shared WEBHOOK_SECRET passed as a query parameter, not a bearer token.

curl -X POST "https://pharosbase.com/api/contacts/event?secret=$WEBHOOK_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
    "projectId": "acme-app",
    "email": "reader@example.com",
    "tag": "signed-up",
    "action": "add"
  }'
Field Type Notes
projectId string Required.
email string Required. Must already exist as a contact.
tag string Required.
action enum add (default) or remove.

Responds { "added": true }, { "removed": true }, or { "noop": true } when the tag was already in the requested state. An unknown email returns 404; the endpoint does not create contacts.

The secret is one value shared across every project, and a secret in a URL tends to end up in access logs and browser history in a way a header does not. Prefer the API-key path for new integrations, and keep this one server-side.