TutorialFollow along and build something end to end. Start here if Bosbec is new to you.

My Events: an example event sign-up app

My Events is an example project you install into your own account: a working sign-up app with a page for creating events, a shareable link your guests use to register, and a REST API behind both. Install it, run one setup trigger, and you have something real in front of you in a few minutes — then open the workflow and change it.

This is not a minimal starting point. It is a complete small application — 85 jobs, six API endpoints, two web pages — so you can see what a finished thing looks like on the platform before building your own. If you want the shortest path to a first response, start with Quickstart: Your first API instead.


GET STARTED FOR FREE


What you get

Part Where it lives
Admin page — create events, see who signed up GET /admin
Sign-up page — what your guests open GET /register?id=…
REST API — events and registrations /api/*
API reference, rendered from the spec GET /docs
Storage — events and participants as Units Two groups in your account

Both pages are served by the workflow itself, from the same HTTP channel as the API. There is no separate web host and nothing to deploy.

Three incoming HTTP triggers for /admin, /register and /docs, each wired to a Send API response job, with the /admin one open showing the page's HTML in its response body, content type text/html and status code 200

A page is just an HTML body on a Send API response job — same mechanism as a JSON endpoint, different content type.

Before you start

You need one thing: an HTTP channel. Create it under Channels and note the subdomain you choose — every URL below is relative to it, for example https://my-events-yourname.in.bosbec.io.

Install and run it

1. Import the template. Open Workflow Builder and navigate to View > Workflow Library. Under Templates, find "My Events" and select use this template. You get one workflow containing nine triggers.

2. Connect the channel. Open each of the eight HTTP triggers and select the channel you created.

3. Name your groups. Open the create_groups trigger and find the Data operations job at the start of it. It holds two Set data operations — the names of the groups that will store your events and your participants. Change them if you like, or keep the defaults.

Use plain names: letters, digits, spaces and hyphens. The lookup that follows treats the name as a regular expression, so brackets and dots can cause a miss.

4. Run create_groups once. Right-click the trigger and choose Start trigger. It looks for each group by name, creates it if it does not exist, and stores the two group IDs in your account settings. Every other job reads the IDs from there, so this is the only place groups are configured.

The create_groups trigger with its Data operations job open, showing two Set data operations writing the group names into metadata.event_group_name and metadata.event_participants_group_name, and the trigger's right-click menu with Start trigger highlighted

The Data operations job holds the two group names. The right-click menu is where you run the trigger.

Running it twice is harmless — it reuses groups that already exist. But do not change the group names after you have created events, or the workflow will start looking in a new, empty group while your data sits in the old one.

5. Open /admin. Create an event. Copy the link it gives you, open it in another tab, and sign yourself up. Go back to the admin page: your name is in the list.

That is the whole loop. Everything from here on is about how it works and what to change.

How a request flows

Every endpoint follows the same shape: read the input, validate it in a chain of routes, touch storage, send a response. Each validation failure has its own Send API response job, which is why the job count is high — 24 of the 85 jobs are responses. Jobs are named below by type and by what they do rather than by their job_nn number, because those numbers shift as soon as you start editing.

The GET /event flow: a route checking for an event id, a Unit pipeline finding units whose event_id metadata matches the query parameter, then JSON pipelines building the response, with a JSON item template mapping the Unit's metadata fields to name, description, start_datetime, nbr_of_spots and participants

GET /api/event end to end. The Unit pipeline finds the event by event_id, and the JSON pipeline's item template is where a Unit's fields become the shape the API returns.

POST /api/register is the most instructive endpoint. Its trigger checks six things in order, each in its own route, and the first failure wins:

Check Failure
Event ID in the query string 400 missing_event_id
Event exists 404 wrong_event_id
Email present and valid 400 missing_or_invalid_email
Not already registered 409 already_registered
Name present 400 missing_name
Spots left 409 event_full

Read the routes from the trigger downwards and they come in that order. Only after the last one does the Unit pipeline write the participant Unit, a Data operations job increment spots_taken, and the final Send API response return 200 success.

The order matters more than it looks. Validate the body before checking capacity, or a guest who forgot to type their name is told the event is full.

Two Unit types

Events and participants are both stored as Units, in the two groups you created.

An event Unit carries event_id, event_name, event_description, event_start_datetime, event_nbr_of_spots and spots_taken. It also sets firstname to the event ID and lastname to the event name, which is what gives the Unit a readable label in lists.

A participant Unit carries email, name and the event_id of the event they signed up for. That event_id is the only link between the two — there is no relation type, just a matching value, and every lookup filters on it.

Reading the API

The full contract is in openapi.yaml, served at /api/openapi.yaml and rendered for reading at /docs. Two conventions worth knowing before you read it:

Successful reads return data directly. Everything else returns { "status": "...", "message": "..." }. Branch on status — it is stable. message is for humans and may be reworded.

Numbers are numbers. nbr_of_spots and spots_taken are integers, not strings.

The same contract in one file, with request bodies, example responses and every status value, is available here:

DOWNLOAD API DOCS

Extend it

The template deliberately stops short of a production system. Here are the natural next steps, roughly in order of difficulty.

Add a field to events

Say you want a location. It touches five places, and three of them are in the POST /api/create trigger, in the order the jobs run:

  1. The request — accept location in the POST /api/create body.
  2. The Parse JSON to resource job — where the incoming JSON is read.
  3. The Unit pipeline — add metadata.event_location to the field mapping, following the defaultvalue(...) pattern the other fields use so a missing value becomes an empty string rather than an error.
  4. The JSON pipeline — include it in the response.
  5. The pages — the form in /admin and the display in /register, each the HTML body of that trigger's Send API response.

Adding a field to participants — dietary requirements, for instance — is the same shape in the POST /api/register trigger: the Parse JSON to resource, the Unit pipeline that writes the participant, and the sign-up form.

Add a registration deadline

Store a registration_closes timestamp on the event, then add a route in the POST /api/register chain that compares it to the current time and returns 409 registration_closed. Put it next to the capacity check — both are questions about whether the event can still accept anyone.

Other ideas

  • Waiting list — when event_full, store the participant with a waiting flag instead of refusing them.
  • Confirmation email — send a message when registration succeeds. You will need the channel's absolute URL for the link, since there is no browser to supply it. Store it in account settings, the way group IDs are stored.
  • Editing events — there is no PUT. Add one.
  • Authentication — the admin page is open to anyone who knows the URL.

When something does not work

Most of the confusion in a workflow like this comes from three platform behaviours that fail silently: the run succeeds, no error appears, and a wrong value carries on. An expression that cannot resolve renders as its own text, a Set data operation with an empty result leaves the old value in place, and a route with Compare value is regex enabled ignores its compare operator.

None of them are specific to this project — you will meet all three in anything you build. When a job succeeded but did the wrong thing has them together, with the detail in Working with Variables and Route From Meta Data.


GET STARTED FOR FREE