Webhooks are part of the Automation module. Each subscription is stored as an automation rule with a single action, "notify URL", so it behaves like any other automation: it can be suspended, resumed and deleted, and every delivery attempt is logged.
Key facts:
Direction: outbound only. Full Fabric calls your endpoint; it does not accept incoming webhooks through this feature.
Method and format:
POSTwith a JSON body (Content-Type: application/json).One event, one subscription: a subscription listens to one event type and posts to one URL. To send the same event to two systems, create two subscriptions.
Where: Automation > Webhooks (
/automation/webhooks), with two tabs: Subscriptions and Activity.
Setting up a webhook
Go to General settings > Webhooks and open the Subscriptions tab.
Click Create subscription.
In Event type, pick the event to listen to. The list is searchable by label (for example "Offer accepted") or by technical topic (for example
automation.offers.accepts).For Profile transition only: optionally choose an Initial state and a Target state. Leave either on Any to match every state. Picking a state without a substate matches all its substates.
In Webhook URL, enter the full address of your endpoint, starting with
https://(for examplehttps://example.com/webhook).Click Create. The subscription appears in the list and starts receiving events immediately.
The subscription list shows, per row: the Trigger (event type, plus the states for profile transitions), the URL, Runs (how many times it has fired) and Created date. The menu at the end of each row lets you Suspend, Resume or Delete it.
Rules applied when you save
Check | Result if it fails |
Event type and URL are both filled in |
|
URL passes the outbound URL safety check (see Security) |
|
Event type is a supported event |
|
No other subscription has the same event type, URL and states |
|
To change the URL of an existing subscription, create a new one and delete the old one. Subscriptions cannot be edited in place.
Suspending versus deleting
Suspend stops deliveries but keeps the subscription and its activity history. Events that happen while it is suspended are not queued and are not sent later.
Resume starts deliveries again for new events.
Delete removes the subscription. System subscriptions created by Full Fabric cannot be deleted.
Event catalogue
Every event below is available in the Event type list. Each table gives the label shown in Full Fabric, the topic sent as event_type, exactly what causes it to fire, and the fields it carries on top of the common envelope (tenant, event_type, message_type, message_id, described in the payload reference). All ids are 24-character hexadecimal strings.
Applications
Event | Topic ( | Fires when | Payload fields |
Application started |
| An application moves into the started state, whether the applicant starts it or staff create it |
|
Application pending payment |
| A started application moves to pending payment because the template requires a fee before submission | Same as Application started |
Application pre-submitted |
| Just before an application is submitted, while the submission is being processed | Same as Application started |
Application submitted |
| An application has been submitted | Same as Application started |
Application unsubmitted |
| Staff revert a submitted application back to unsubmitted |
|
Application updated |
| Answers in an application are saved. Batched: at most one event per application every 20 seconds, carrying the latest state |
|
Application template closed |
| An application template passes its closing date. Checked every minute, only for templates that have active automations |
|
primary_choice_context describes the applicant's first choice: context_id and context_type (the class chosen), campus_id, and children (courses picked within that choice). It is null when no choice was made.
Offers
Event | Topic ( | Fires when | Payload fields |
Offer made |
| An offer is sent to an applicant |
|
Offer accepted conditionally |
| The applicant accepts an offer that still has conditions to meet | Same as Offer made |
Offer submitted |
| The applicant submits the offer form | Same as Offer made |
Offer accepted |
| The offer is accepted | Same as Offer made |
Offer declined |
| The offer is declined | Same as Offer made |
Profiles and lifecycle
Event | Topic ( | Fires when | Payload fields |
Profile transition |
| A profile gets a new role in a class, or an existing role changes lifecycle state. Filtered by the Initial and Target states you chose. Withdrawals do not trigger it |
|
Profile role removed |
| A profile's role in a class is removed |
|
Profile withdrawn |
| Staff withdraw a profile from a class, individually or in bulk |
|
Profile activated |
| A profile becomes active: sign-up confirmed, first SSO login, or staff activation |
|
Profile deactivated |
| A profile is set to inactive |
|
Profile suspended |
| A profile is suspended by staff, through the API, or because its access period expired at login |
|
States are written as state::substate, for example applicant::submitted. A Profile transition with an Initial state set only fires on changes, never when a role is first added, because a new role has no previous state.
Forms
Event | Topic ( | Fires when | Payload fields |
Form submission |
| Any form in the institution is submitted. A subscription created here is not limited to one form: filter on |
|
Evaluations
Event | Topic ( | Fires when | Payload fields |
Evaluation assigned |
| An evaluation is created or assigned to an evaluator, including bulk assignment |
|
Evaluation submitted |
| An evaluator submits an evaluation | Same as Evaluation assigned |
Events
Event | Topic ( | Fires when | Payload fields |
Event response |
| Someone registers for an event for the first time. Changes to an existing response do not trigger it |
|
Event reminder |
| A scheduled event reminder is sent. Fires once per attendee who answered Yes or Maybe |
|
Payments
Event | Topic ( | Fires when | Payload fields |
Payment plan published |
| A payment plan is published to the student for the first time |
|
Payment plan paid |
| All fees of a payment plan are paid | Same as Payment plan published |
Payment plan deleted |
| A payment plan is deleted | Same as Payment plan published |
First fee paid |
| The first fee of a payment plan becomes paid |
|
Payment plan fee paid |
| Any fee becomes paid, by a completed transaction or a discount covering it. Batched per fee over 5 seconds | Same as First fee paid |
Payment plan fee deleted |
| A fee is deleted from a payment plan | Same as First fee paid |
Academic affairs
Event | Topic ( | Fires when | Payload fields |
Registration |
| A student's course registration becomes registered, either created directly or confirmed from conditional |
|
Unregistration |
| A course registration is rejected or deleted, including when the profile is anonymised |
|
Class change |
| A class's settings are edited and saved |
|
Billing
Event | Topic ( | Fires when | Payload fields |
Billing contract created |
| A billing contract is created |
|
Billing contract updated |
| A billing contract is changed |
|
Payload reference
Each delivery is one POST with header Content-Type: application/json and a flat JSON object. It holds the event's own fields (see the catalogue) plus four envelope fields that are always present.
Field | Type | Meaning |
| string | The topic that fired, for example |
| string | Your institution's handle, the subdomain of your Full Fabric site. Useful when one endpoint serves several institutions. |
| string (UUID) | Unique id of this event. Use it to detect duplicates. |
| string | Internal channel name. It can carry your handle as a prefix and dashes instead of dots ( |
Value formats:
Ids are 24-character hexadecimal strings, for example
65f1c0e2a4b3d90012ab34cd. Use them with the Full Fabric API to fetch the full record.Dates and times are ISO 8601 strings in UTC, for example
2026-10-15T09:42:17.123Z.Lifecycle states are
state::substatestrings.A field without a value is sent as
nullor left out.
Payloads carry ids, not full records. To get an applicant's name or an application's answers, call the API with the id you received. Form submissions and event responses are the exception: they include the answers given.
Example: Application submitted
{ "user_id": "65f1c0e2a4b3d90012ab34cd", "profile_id": "65f1c0e2a4b3d90012ab34cd", "applic_id": "6710a3b9c2e4f50013de5678", "template_id": "66a2b1c0d9e8f70011aa9012", "primary_choice_context": { "context_id": "64b0a1b2c3d4e50010bc7890", "context_type": "Institutions::ClassOf", "campus_id": "64b0a1b2c3d4e50010bc1111", "children": [] }, "tenant": "acme", "message_type": "acme-automation-applics-submissions", "message_id": "3f6d2a8e-9b41-4c55-8a0e-1d2c3b4a5f60", "event_type": "automation.applics.submissions" }Example: Profile transition
{ "profile_id": "65f1c0e2a4b3d90012ab34cd", "role_id": "65f1c0e2a4b3d90012ab4400", "context_type": "Institutions::ClassOf", "context_id": "64b0a1b2c3d4e50010bc7890", "previous_state_and_substate": "applicant::submitted", "previous_withdrawn": false, "state": "applicant", "substate": "accepted", "state_and_substate": "applicant::accepted", "withdrawn": false, "user_id": "6600aa11bb22cc33dd44ee55", "tenant": "acme", "message_type": "acme-automation-profiles-roles-changes", "message_id": "a1b2c3d4-e5f6-4711-8899-aabbccddeeff", "event_type": "automation.profiles.roles.changes" }Treat any field you do not use as optional, and ignore unknown fields: new fields may be added without notice.
Delivery, retries and activity log
Events are delivered in the background, usually within 5 to 30 seconds of the action, and at most 3 attempts are made per event. Nothing waits for your endpoint: a slow or failing endpoint never blocks the user who triggered the event.
How a delivery happens
The action happens in Full Fabric (for example an offer is accepted).
Full Fabric matches the event against active subscriptions and queues one delivery per match.
A background worker picks queued deliveries up every 5 seconds and posts them to your URL.
Application updated and Payment plan fee paid add a batching window (20 seconds and 5 seconds) before step 2.
Timeouts and retries
Setting | Value |
Connection timeout | 5 seconds |
Response timeout | 10 seconds |
Attempts | 3 in total (1 + 2 retries) |
Wait before retry 1 | 5 seconds |
Wait before retry 2 | 15 seconds |
What is retried depends on your endpoint's answer:
Endpoint answer | Outcome |
| Success. Logged once. |
| Treated as final. Logged once and not retried. Redirects are not followed. |
| Logged and retried. |
Timeout, refused connection, DNS or TLS error | Logged and retried. |
After the third failed attempt the event is dropped and marked as failed. Full Fabric does not replay it later, and the subscription stays active for the next events. There is no automatic suspension after repeated failures.
Delivery is at least once and not ordered. Two events about the same record can arrive in either order, and a retry after a timeout can deliver an event your endpoint already processed. Deduplicate on message_id and, when order matters, re-read the record from the API.
Activity log
The Activity tab (Automation > Webhooks > Activity) lists every delivery attempt across all subscriptions, newest first:
Column | Shows |
Trigger | The event type and subscription that fired |
URL | Where the request was sent |
Status | Your endpoint's HTTP status code, empty when no response was received |
Payload | The exact JSON body that was sent |
Date | When the attempt was made |
Each attempt has its own row, so a delivery that succeeded on the second try shows a failed row and a successful row. When no response came back (timeout, connection error), the row records the error message instead of a status. The log also stores your endpoint's response body, truncated to 10,000 characters.
Security
Requests are not signed and carry no authentication header, so your endpoint must treat every call as untrusted until checked. Use these measures together:
Use HTTPS. Plain
http://URLs are accepted but send personal data in clear text.Use a hard-to-guess URL. Include a long random token in the path, for example
https://example.com/hooks/ff/7Qm2…, and reject calls to any other path. Anyone who can see the subscription list or the activity log can see this URL, so give webhook access only to staff who need it.Verify before acting. Use the ids in the payload to fetch the record from the Full Fabric API with your own credentials, and act on what the API returns rather than on the payload alone.
Deduplicate on
message_id.Minimise data. Payloads carry personal data (profile ids, and form or registration answers for those events). Store only what you need and apply your own retention rules.
When a subscription is saved, Full Fabric rejects URLs that are not http or https, have no host, or point at cloud metadata addresses (169.254.x.x, metadata.google.internal).
Permissions
Managing webhooks requires the Automation access feature on the staff member's role. The same feature gives access to the activity log, which shows the payloads sent.
Troubleshooting
Symptom | Likely cause and fix |
No rows in the Activity tab | The event has not happened since the subscription was created, the subscription is suspended, or the action did not match its filters (for example the states of a Profile transition). Trigger the action yourself and wait 30 seconds. |
Nothing is delivered in a sandbox | Webhook deliveries run only in live institutions. Sandboxes do not process automations, so test against your live site with a test record. |
Rows show status | Your endpoint rejected the request. Check its path, authentication and body parsing. 4xx answers are not retried. |
Rows show an error and no status | Full Fabric could not reach your endpoint: DNS, TLS certificate, firewall or a reply slower than 10 seconds. Answer |
The same event arrives twice | A retry after a timeout or a 5xx. Deduplicate on |
A Profile transition with an Initial state never fires when a student is first added to a class | Expected: adding a role has no previous state. Leave Initial state on Any to catch additions. |
Form submission fires for every form | Expected: the subscription covers all forms. Filter on |
Changing an event registration sends nothing | Expected: Event response fires only for new registrations. |
| A subscription with the same event, URL and states exists. Use it, or vary the URL. |
