fabIAna

Connect your product to Fabiana

Tell Fabiana when people sign up or do something in your product. She keeps the contact up to date, and your automations send the right email at the right time.

Authentication

Create a key in Settings → API keys (Pro and Business plans). It is shown once. Send it on every request:

Authorization: Bearer fab.…

The base URL is https://api.getfabiana.com. Requests and responses are JSON. Each organization can make 300 requests a minute.

Send events

One call per thing that happened. Fabiana finds the contact by email or phone, or creates it, and saves the properties on the contact as custom fields.

curl -X POST https://api.getfabiana.com/v1/events \
  -H "Authorization: Bearer $FABIANA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "event": "signed_up",
    "contact": { "email": "ana@example.com", "name": "Ana", "emailOptIn": true },
    "properties": { "plan": "pro", "seats": 3 }
  }'
{ "contactId": "6f1c…", "created": true }

What happens next

  • The event appears on the contact's timeline.
  • Automations with the trigger Event is received run, filtered by event name and properties.
  • Automations with the trigger Custom field changes run when a property changes value.
  • Sequences waiting for this event move on; sequences set to stop on it stop.
  • A new contact also starts the Contact is created automations.

Event names use letters, digits and _ . : -. Properties are flat: up to 50 strings, numbers or booleans.

Emails from your events

Automations can start an email sequence or send a single email, written as text or built from a template with your logo and colour. Subjects and bodies can use {{first_name}}, {{name}}, {{email}} and any property as {{custom.plan}}. Sequence steps can wait days or hours, wait until an event or a property, or branch on a condition.

Marketing emails only go to contacts who agreed to receive them or are already customers, and carry an unsubscribe link. Send emailOptIn: true when the person ticked the box in your sign-up form.

A single email marked Transactional, for the person’s own account or request (a welcome, a receipt), is sent without that consent and without an unsubscribe link. Never use it for promotions.

Add your domain in Settings → Email sending and copy the DNS records it shows. Once verified, emails go out from your own address, signed for your domain.

Contacts

POST/v1/eventsRecord something a contact did in your product
POST/v1/contactsCreate a contact, or get the one with the same email or phone
GET/v1/contactsList contacts, 50 at a time (limit, cursor)
GET/v1/contacts/{id}Get one contact
PATCH/v1/contacts/{id}Update a contact
curl -X POST https://api.getfabiana.com/v1/contacts \
  -H "Authorization: Bearer $FABIANA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "email": "ana@example.com", "name": "Ana", "tags": ["trial"], "properties": { "plan": "pro" } }'

The full reference is in the OpenAPI description, which most API tools and coding assistants can read.

Webhooks

Add an endpoint in Settings → Webhooks and pick the events you want. Fabiana sends a POST for each one and retries when your server fails.

  • contact.created: A contact was created, by Fabiana, the team, an import or the API
  • contact.updated: A contact changed; changedFields lists what
  • contact.stage_changed: A contact moved to another pipeline stage (also sent as deal.stage_changed)
  • lead.created: Fabiana captured a new lead in a conversation
  • lead.converted: A contact reached the booked stage for the first time
  • deal.closed: A deal was closed as won (also sent as deal.closed_won)
  • deal.closed_lost: A contact moved to the lost stage
  • conversation.created: A new conversation started (also sent as conversation.started)
  • message.received: A customer wrote on any channel
  • handoff.requested: Fabiana passed a conversation to the team (also sent as conversation.handoff)
  • handoff.claimed: Someone on the team took over a conversation
  • handoff.resolved: A conversation the team had taken over was resolved
  • conversation.resolved: A conversation was marked resolved
  • conversation.closed: A conversation was archived
  • csat.submitted: A customer rated a conversation
{
  "event": "deal.closed",
  "organizationId": "…",
  "timestamp": "2026-10-29T10:00:00.000Z",
  "data": { "contactId": "…", "name": "Ana", "dealClosedValue": 1200, "dealClosedAt": "…", "stage": "booked" }
}

Check the X-Fabiana-Signature header against the raw body and the webhook's secret:

import { createHmac, timingSafeEqual } from 'node:crypto'

function isFromFabiana(rawBody: string, header: string, secret: string): boolean {
  const expected = 'sha256=' + createHmac('sha256', secret).update(rawBody).digest('hex')
  return header.length === expected.length && timingSafeEqual(Buffer.from(header), Buffer.from(expected))
}

Errors

400 the request is not valid, 401 the key is missing or revoked, 403 the key's role or your plan does not allow it, 404 no such contact, 429 too many requests. The body has a message.

Questions? Write to hello@getfabiana.com.