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 to | Use |
|---|---|
| Hear about each registration | A workflow with the Registered for an event trigger and a Webhook step |
| Read one registration | GET /v1/registrations/{registrationId} |
| List an event's registrations | GET /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
}| Field | What it is |
|---|---|
type | Always workflow.step. |
id | The same on every try of one call. Skip an id you've already handled. |
workflowId | The workflow. Its ⋯ menu has Copy workflow ID. |
runId | This person's run through the workflow. |
stepId | The step that called. |
contactId | The contact going through the workflow, or null if they've been deleted since. |
registrationId | The registration that started the run, in a workflow started by Registered for an event. Otherwise null. |
bookingId | The booking that started the run, in a workflow started by Booking made. Otherwise null. |
groupRowId | The 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
2xxstatus 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
idyou'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
| Field | What it is |
|---|---|
status | pending while a payment is going through or you haven't decided on a request, then confirmed, expired or cancelled. |
approval | not_required, or for an event that takes requests, pending, approved or declined. |
contactId | The buyer's contact. Each seat in attendees names its own, since a buyer can register friends. |
totalCents, discountCents | What the registration came to after any coupon, and the discount. |
paymentStatus | How the payment stands, like succeeded or refunded. null for a free registration. |
attendees | One 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"| Query | What it does |
|---|---|
status | Only registrations with this status. Left off, every status but expired. |
occurrenceId | Only registrations for one date of the event. |
q | Only buyers whose name or email address contains this. |
limit, offset | See Pagination. |
The response has items, total, limit and offset, and each item is a registration as above.
Errors
| Error | When | What to do |
|---|---|---|
403 Forbidden | The key doesn't have events:read | Create a key with events:read. See Authentication |
404 NotFound | No registration has that ID, or no event has that short code, among the events you run or cohost | Check the ID or the short code |
See Events in the API reference for every field.