Workflow webhooks

Have a workflow call your own system when someone registers for an event, then read their registration through the API.

Use this when your own system needs to act on something that happens in Lahuta, like creating a student account for each person who registers for a class. A workflow's Webhook step calls an address on your system. The call carries IDs only, and your system reads the details through the API with its own key.

You want toUse
Hear about each registrationA workflow with the Registered for an event trigger and a Webhook step
Read one registrationGET /v1/registrations/{registrationId}
List an event's registrationsGET /v1/events/{shortCode}/registrations

Both endpoints need a key with events:read. See Authentication.

Set up the workflow

In Marketing → Workflows, create a workflow with the Registered for an event trigger and pick the event.

Add a Webhook step. Under URL, enter your endpoint's address with a secret in it that only your system knows, like https://northsidepottery.com/api/lahuta/7f3k9q2m. The address must start with https://.

Publish the workflow. From then on, each registration that goes through starts a run, and the run calls your endpoint when it gets to the step.

The step works in workflows with any trigger. See Workflows for the step itself.

What your endpoint receives

Lahuta sends a POST with a JSON body:

{
  "type": "workflow.step",
  "id": "workflow:0197c4d2-8a1e-7b3f-9c5d-2e6f8a1b4c7d:k3m9q2xa:0",
  "workflowId": "0197b3a1-6c0e-7d42-9a1f-3e8b5c2d7f14",
  "runId": "0197c4d2-8a1e-7b3f-9c5d-2e6f8a1b4c7d",
  "stepId": "k3m9q2xa",
  "contactId": "0197c4d2-7f10-7a2e-8b4c-1d3e5f7a9b2c",
  "registrationId": "0197c4d2-7e5b-7c1d-9a3e-4f6b8c2d1e5a",
  "bookingId": null,
  "groupRowId": null
}
FieldWhat it is
typeAlways workflow.step.
idThe same on every try of one call. Skip an id you've already handled.
workflowIdThe workflow. Its ⋯ menu has Copy workflow ID.
runIdThis person's run through the workflow.
stepIdThe step that called.
contactIdThe contact going through the workflow, or null if they've been deleted since.
registrationIdThe registration that started the run, in a workflow started by Registered for an event. Otherwise null.
bookingIdThe booking that started the run, in a workflow started by Booking made. Otherwise null.
groupRowIdThe group row that started the run, in a workflow started by Added to a group. Otherwise null.

The body never holds the person's details, so a call your endpoint didn't expect can't tell it anything your key couldn't.

Answer the call

  • Check the secret in the path, and refuse the call if it's wrong.
  • Answer with any 2xx status within 10 seconds. Do slow work after you answer.
  • If your endpoint answers anything else, or doesn't answer in time, Lahuta tries again after a minute, then waits longer each time, up to two hours between tries. After a day it gives up and the run moves on. The run's timeline in View runs shows what each try came to.
  • The same call can arrive more than once, so skip an id you've already handled.
  • Redirects aren't followed. Use the address your endpoint answers on.

Read the registration

GET /v1/registrations/{registrationId} returns one registration with its seats. Here Northside Pottery's site receives the call and reads the registration it names:

curl https://api.lahuta.org/v1/registrations/0197c4d2-7e5b-7c1d-9a3e-4f6b8c2d1e5a \
  -H "X-Api-Key: $LAHUTA_API_KEY"
{
  "id": "0197c4d2-7e5b-7c1d-9a3e-4f6b8c2d1e5a",
  "eventId": "0197a8f0-3b2c-7d1e-8f4a-6c9b2e1d5a3f",
  "occurrenceId": "0197a8f0-3b40-7a2c-9e1d-4b6f8a2c3d5e",
  "contactId": "0197c4d2-7f10-7a2e-8b4c-1d3e5f7a9b2c",
  "buyerName": "Priya Raman",
  "buyerEmail": "priya.raman@example.com",
  "status": "confirmed",
  "approval": "not_required",
  "totalCents": 4500,
  "discountCents": 0,
  "couponCode": null,
  "paymentStatus": "succeeded",
  "attendees": [
    {
      "id": "0197c4d2-7f22-7b3d-8c5e-2f4a6b8c1d3e",
      "occurrenceId": "0197a8f0-3b40-7a2c-9e1d-4b6f8a2c3d5e",
      "ticketId": "0197a8f0-3b55-7c4e-8a2f-5d7b9c1e3f4a",
      "ticketName": "Wheel-throwing class",
      "contactId": "0197c4d2-7f10-7a2e-8b4c-1d3e5f7a9b2c",
      "name": "Priya Raman",
      "email": "priya.raman@example.com",
      "phone": null,
      "priceCents": 4500,
      "status": "active",
      "checkedInAt": null,
      "createdAt": "2026-10-09T15:42:10.512Z"
    }
  ],
  "createdAt": "2026-10-09T15:42:10.498Z"
}

Registration fields

FieldWhat it is
statuspending while a payment is going through or you haven't decided on a request, then confirmed, expired or cancelled.
approvalnot_required, or for an event that takes requests, pending, approved or declined.
contactIdThe buyer's contact. Each seat in attendees names its own, since a buyer can register friends.
totalCents, discountCentsWhat the registration came to after any coupon, and the discount.
paymentStatusHow the payment stands, like succeeded or refunded. null for a free registration.
attendeesOne per seat: who it's for, the ticket, what it cost, whether it's active or cancelled, and when they checked in.

List an event's registrations

GET /v1/events/{shortCode}/registrations returns an event's registrations, newest first, 25 at a time. The short code is the last part of the event's link: for lahuta.org/events/k7Qm2x, it's k7Qm2x. Use it to catch up on anything your endpoint missed, like calls from while it was down for longer than a day.

curl "https://api.lahuta.org/v1/events/k7Qm2x/registrations?status=confirmed&limit=50" \
  -H "X-Api-Key: $LAHUTA_API_KEY"
QueryWhat it does
statusOnly registrations with this status. Left off, every status but expired.
occurrenceIdOnly registrations for one date of the event.
qOnly buyers whose name or email address contains this.
limit, offsetSee Pagination.

The response has items, total, limit and offset, and each item is a registration as above.

Errors

ErrorWhenWhat to do
403 ForbiddenThe key doesn't have events:readCreate a key with events:read. See Authentication
404 NotFoundNo registration has that ID, or no event has that short code, among the events you run or cohostCheck the ID or the short code

See Events in the API reference for every field.

On this page