Create an approval form
Describe the decision as JSON. Give it a title, an optional description and a list of fields.
curl -X POST https://aisenseapi.com/services/v1/webhook_action \
-H "Content-Type: application/json" \
-d '{
"title": "Approve invoice INV-2043?",
"description": "Vendor total exceeds the auto-approval limit.",
"fields": [
{
"type": "radio",
"name": "decision",
"label": "Decision",
"required": true,
"options": [
{"value":"approve","label":"Approve"},
{"value":"reject","label":"Reject"}
]
},
{"type":"textarea","name":"comment","label":"Notes","max_length":500}
]
}'
{
"ok": true,
"action_id": "c2bcee60-016d-419e-a252-4e60600a2e2e",
"form_url": "https://aisenseapi.com/services/v1/webhook_action/c2bcee60-016d-419e-a252-4e60600a2e2e/form",
"result_url": "https://aisenseapi.com/services/v1/webhook_action/c2bcee60-016d-419e-a252-4e60600a2e2e",
"wait_url": "https://aisenseapi.com/services/v1/webhook_action/c2bcee60-016d-419e-a252-4e60600a2e2e/wait/25",
"expire_timestamp": 1787266368,
"expire_datetime": "2026-08-20T22:52:48Z"
}
One call creates three things. The form_url is a real HTML page that opens in any browser. The result_url returns JSON for your workflow. The action_id appears inside both, so a single string is all you need to keep.
Nothing else is required. No key travels with the request, you host no receiver, and you open no inbound port.
Ask a group
Add "respondents": 3 to create three separate form links. The create response returns form_urls instead of form_url. Each link works once and stores only a hash of its bearer token.
{
"title": "Choose the release window",
"respondents": 3,
"fields": [
{"type":"radio","name":"decision","label":"Window","required":true,
"options":["morning","evening"]}
]
}The group result starts at pending, moves to partial after the first answer and becomes answered after all links are used. It reports respondents, answered, tally and responses. The tally counts the field named decision.
Read, wait or receive a signal
A plain GET returns immediately. Add /wait/25 to wait for up to 25 seconds. A group wait also returns as soon as one new answer changes the state.
curl https://aisenseapi.com/services/v1/webhook_action/c2bcee60-016d-419e-a252-4e60600a2e2e/wait/25
{
"ok": true,
"action_id": "c2bcee60-016d-419e-a252-4e60600a2e2e",
"status": "pending",
"created_at_timestamp": 1787179968,
"created_at_datetime": "2026-08-19T22:52:48Z",
"expire_timestamp": 1787266368,
"expire_datetime": "2026-08-20T22:52:48Z",
"answered_at_timestamp": null,
"answered_at_datetime": null,
"response": null
}
Until someone answers, status reads pending and response is null. A waiting response adds waited_seconds and wait_reason.
Now the reviewer opens the form and submits it. The same URL changes shape.
{
"ok": true,
"action_id": "c2bcee60-016d-419e-a252-4e60600a2e2e",
"status": "answered",
"created_at_timestamp": 1787179968,
"created_at_datetime": "2026-08-19T22:52:48Z",
"expire_timestamp": 1787266368,
"expire_datetime": "2026-08-20T22:52:48Z",
"answered_at_timestamp": 1787179994,
"answered_at_datetime": "2026-08-19T22:53:14Z",
"response": {
"decision": "approve",
"comment": "Budget confirmed by finance"
}
}
Every answer sits under response, keyed by the name you gave that field when you created the action. Group actions return the same objects inside responses.
You may add notify_url to the create body. After the single answer, or after the final group answer, that public URL receives one signal with the action ID, result URL and answer counts. It does not receive the answers themselves.
Field types the free webhook action API endpoint accepts
Five types cover the decisions most pipelines hand to a person.
| Type | Best for | Important properties |
|---|---|---|
| radio | One visible choice from a short list | options, required |
| select | One choice from a longer list | options, required |
| text | Short free-form answers | max_length, required |
| textarea | Comments, reasons and instructions | max_length, required |
| checkbox | Confirmation or several independent choices | options, required |
Options take either of two shapes. Plain strings work when the stored value and the visible label are the same. Objects with separate value and label properties work when they differ, which keeps machine-readable codes out of the reviewer's view.
{
"title": "Route the support ticket",
"fields": [
{"type":"select","name":"queue","label":"Queue","required":true,
"options":["billing","technical","sales"]},
{"type":"text","name":"owner","label":"Assign to","max_length":40},
{"type":"checkbox","name":"flags","label":"Flags",
"options":["urgent","needs_manager"]}
]
}
That body builds a routing form with a required queue, a free-text owner capped at forty characters, and two flags the reviewer can tick independently.
Response fields
A read returns the same core fields before and after an answer.
| Field | Type | Description |
|---|---|---|
| action_id | string | The UUID that identifies this action. |
| status | string | pending, partial for a group, or answered. |
| created_at_timestamp | integer | Unix time when the action was created. |
| expire_timestamp | integer | Unix time exactly 24 hours after creation. |
| answered_at_timestamp | integer | Unix time when the action became complete, or null. |
| response | object | The single submitted response, or null. |
| respondents | integer | Required answer count for a group. |
| answered | integer | Answers received for a group. |
| tally | object | Counts for the group field named decision. |
| responses | array | Completed group responses without bearer tokens. |
Errors and repeat submissions
An unknown action ID answers with HTTP 404. An expired action answers with HTTP 410.
{"error":"Action id unknown"}
A second submission on a form that already carries an answer is refused with HTTP 409.
{"error":"Action already answered"}
That refusal earns its place. Each action is a one-shot decision, so a reviewer who reloads the page cannot quietly overwrite a verdict your pipeline has already acted on. The free webhook action API endpoint keeps the first answer and rejects the rest.
How a pause for a human works
Three steps sit between an automated run and a human verdict.
- Describe the decision
POST a title, an optional description and one or more fields. The reply carries the two URLs you need.
- Deliver the form URL
Send it through email, chat, a ticket comment or whatever channel the reviewer already watches. They open a page and press submit. There is no login and no app to install.
- Poll until the status changes
Resume the workflow when
statusflips toanswered, and branch on the values insideresponse.
The hosted page is deliberately ordinary. It is a plain HTML form that posts back to its own address, and it carries no script tags at all. That keeps it usable in a locked-down corporate browser or on a phone, and it means a reviewer who has never seen your pipeline still knows what to do with it.
Where a human decision belongs
Teams reach for the free webhook action API endpoint whenever a rule cannot settle something safely on its own. Four patterns come up again and again.
Deployment gates
Require an explicit go or no-go before a production change continues.
Agent escalation
Let an AI agent ask a person when it reaches a risky or ambiguous step.
Content review
Collect approval, rejection and revision notes in one predictable shape.
Exception handling
Pause an automation when a transaction or document needs human judgment.
The pattern also pairs well with an inbound listener. Capture a third-party callback with the webhook capture API endpoint, inspect what arrived, then raise an action when the payload looks wrong.
Expiry, security and limits
Actions expire 24 hours after creation. Design the caller to handle an action that is never answered, rather than polling forever.
Both URLs are unguessable capability URLs. Anyone holding one can open the form or read the answer, so send them to the intended reviewer and to nobody else. Confidential material, long retention and account-based access control belong in a system built for those things.
Create bodies are limited to 64 KB. A title may be 200 characters, a description 5000 characters and a form 20 fields. One field may have 50 options. Group actions accept 1 to 20 respondents.
The base URL is https://aisenseapi.com/services/v1. No key and no account are required. Every service on the free public REST APIs hub shares one limit of 5000 requests per IP per 24 hours. Use the wait URL instead of a tight polling loop.
Neighbouring services fill the gaps. Park a large payload in the storage API endpoint and put only its id in the description. Shorten a long form link with the URL shortener API endpoint before pasting it into a chat message. Check a payload against a rule with the validation API endpoint first, and raise an action only for the cases that fail. Used that way, the free webhook action API endpoint becomes the one step in a pipeline where a person is genuinely required.