Developers

Partner Booking API

The Avena Flow Partner Booking API lets marketplaces, calendars, and integration platforms read a tenant’s services + availability and create bookings on their behalf. Two-way sync via signed webhooks keeps your system aligned with the tenant’s live calendar.

v1JSONHMAC-SHA256 webhooksStable

Authentication

Every request must include a Bearer token. Tenants generate keys self-serve from Settings → Integrations in their Avena Flow dashboard. Keys are scoped to one tenant and can be revoked instantly.

Authorization: Bearer afp_live_8k3...

Key format: afp_live_ prefix + 32 random bytes (URL-safe base64). Total length ≈ 52 chars.

List services

GET/api/v1/public/services

Returns active, bookable services for the authenticated tenant.

curl https://avenaflow.com/api/v1/public/services \
  -H "Authorization: Bearer afp_live_..."

Response

{
  "data": [
    {
      "id": "a7b1...",
      "name": "Microblading Touch-Up",
      "description": "Color refresh, 1 year follow-up.",
      "category": "PMU",
      "duration_minutes": 90,
      "cleanup_minutes": 15,
      "price_cents": 25000,
      "currency": "USD",
      "image_url": "https://...",
      "service_type": "treatment"
    }
  ],
  "meta": { "tenant_id": "...", "partner_label": "ClassPass", "count": 12 }
}

Get availability

GET/api/v1/public/availability

Returns open slots over a date range. Honors the tenant’s working hours, blocks, and lead-time rules.

ParamTypeNotes
service_idstringrequired — UUID from /services
fromYYYY-MM-DDoptional — defaults to today
toYYYY-MM-DDoptional — defaults to from + 14d (max 60)
staff_idstringoptional — filter to one staff member
curl "https://avenaflow.com/api/v1/public/availability?service_id=a7b1&from=2026-06-10&to=2026-06-17" \
  -H "Authorization: Bearer afp_live_..."

Response

{
  "data": {
    "service": { "id": "a7b1", "name": "Microblading Touch-Up", "duration_minutes": 90, "price_cents": 25000 },
    "from": "2026-06-10",
    "to": "2026-06-17",
    "timezone": "America/New_York",
    "days": [
      {
        "date": "2026-06-10",
        "day_of_week": "Wednesday",
        "slots": [
          { "start": "2026-06-10T10:00:00.000Z", "end": "2026-06-10T11:30:00.000Z" },
          { "start": "2026-06-10T14:00:00.000Z", "end": "2026-06-10T15:30:00.000Z" }
        ]
      }
    ]
  }
}

Create a booking

POST/api/v1/public/bookings

Creates a confirmed appointment in the tenant’s calendar. Auto-creates the contact if their email/phone doesn’t match an existing client. Returns 409 if the slot was taken between your availability check and this call.

Idempotency: include partner_reference_id. Re-POSTing the same value returns the original booking instead of creating a duplicate.

curl -X POST https://avenaflow.com/api/v1/public/bookings \
  -H "Authorization: Bearer afp_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "service_id": "a7b1...",
    "start_time": "2026-06-10T14:00:00Z",
    "customer": {
      "first_name": "Jane",
      "last_name": "Doe",
      "email": "jane@example.com",
      "phone": "+14015551234"
    },
    "partner_reference_id": "classpass_res_42",
    "notes": "First-time client, allergic to lidocaine"
  }'

Response (201)

{
  "data": {
    "id": "ap_9f2...",
    "status": "confirmed",
    "service_id": "a7b1...",
    "contact_id": "co_4d1...",
    "start": "2026-06-10T14:00:00",
    "end": "2026-06-10T15:30:00",
    "price_cents": 25000,
    "currency": "USD",
    "partner_source": "classpass",
    "partner_reference_id": "classpass_res_42"
  }
}

Cancel a booking

DELETE/api/v1/public/bookings/{id}

Soft-cancels a booking — status flips to cancelled and the slot reopens. You can only cancel bookings created by the same partner key.

curl -X DELETE "https://avenaflow.com/api/v1/public/bookings/ap_9f2?reason=customer_request" \
  -H "Authorization: Bearer afp_live_..."

Webhooks & signature verification

When the tenant cancels, reschedules, or otherwise changes a booking inside Avena Flow, we POST a signed event to every active webhook endpoint they’ve registered with you.

Event types

  • booking.created
  • booking.cancelled
  • booking.rescheduled
  • service.updated
  • availability.changed

Request headers

POST /your/webhook HTTP/1.1
Content-Type: application/json
X-Avena-Signature: t=1717084800,v1=4f7b2...
X-Avena-Event: booking.cancelled
User-Agent: AvenaFlow-Webhooks/1.0

Verify the signature

Concatenate {timestamp}.{raw_body} and HMAC-SHA256 it with your signing secret. Constant-time compare against the v1 value. Reject any timestamp older than 5 minutes to prevent replay.

import crypto from 'crypto'

function verify(rawBody, header, secret) {
  const parts = Object.fromEntries(header.split(',').map((p) => p.split('=')))
  const ts = Number(parts.t)
  const expected = crypto
    .createHmac('sha256', secret)
    .update(`${ts}.${rawBody}`)
    .digest('hex')
  return crypto.timingSafeEqual(Buffer.from(parts.v1), Buffer.from(expected))
}

Retry policy

Non-2xx responses or timeouts (10s) are retried at 1m, 5m, 30m, 2h. After 5 failed attempts we mark the delivery failed. After 3 consecutive failures across deliveries we auto-disable the endpoint until the tenant re-enables it.

iCal feed

GET/api/v1/public/calendar/{slug}.ics

Read-only iCal feed scoped by the tenant’s booking slug. No auth needed — the URL itself is the credential. Useful for partners that only need read access (Google Reserve discovery, Yelp Book, calendar mirrors).

Errors

All errors return a JSON body with an error key and (when helpful) a message.

StatusErrorMeaning
400missing_required_fieldsBody missing service_id, start_time, or customer.first_name
401missing_bearer_tokenNo Authorization header
401invalid_api_keyKey not found or hash mismatch
401api_key_revokedKey was revoked by the tenant
403insufficient_scopeKey lacks the required scope
404service_not_foundService doesn’t exist or isn’t active
409slot_unavailableSlot was taken between your availability check and the booking