Developer Reference
This page is for developers who connect OmniForm to their own systems. You may need to build two endpoints:
A choices endpoint that returns the choices for a dropdown, if your admin loads choices from your API.
A webhook that receives each form submission.
How admins set up forms is described in Build a Form.
Choices endpoint
The Nested API Dropdown, and Dropdown and Multi-select with From API as their choices source, load their choices from an endpoint you provide.
Response format
Return a JSON array of objects with label and value:
labelis the text the agent sees.valueis what your webhook receives for that choice, for example an ID from your system.Return the array itself, not an object that wraps it (such as
{ "data": [...] }). Other shapes are not displayed correctly.For the Nested API Dropdown, if the request fails or the response is not valid JSON, the agent sees Failed to get options.
For Dropdown and Multi-select with From API as their choices source:
labelandvaluemay be text or numbers.valueis always sent to your webhook as text.Items with the same
valueare shown once. Items with an emptyvalueare skipped.If the request fails, or the response is not an array of
label/valueobjects, the agent sees Couldn't load choices. Close and reopen this list to try again. They can still remove choices they already selected and submit the form.If the response is an empty array, the list shows that there is no data.
A choice the agent selected earlier that your endpoint no longer returns stays in the field, marked Not in list. It is still sent to your webhook unless the agent removes it, and it counts toward the Choice Limit.
How the request is sent
Method:
GET, with the headers and query parameters configured on the field.Added automatically: the query parameters
room_id(the room the agent is working in) andapp_id(your Omnichannel App ID). Test API in the builder does not sendroom_id, because no room is open.From the agent's browser: the request is sent directly from the browser of the agent who opens the form, not from Qiscus servers. Your endpoint must be reachable from the agent's network and must allow cross-origin requests (CORS) from the Omnichannel agent dashboard, including the headers configured on the field.
Headers are visible: because the request runs in the browser, the configured header values (for example an API key) can be seen with the browser's developer tools by anyone who opens the form. They are also included in the webhook body, in the field's
payload. Use a key that can only read this list.When it is called: when the field is shown to the agent. Searching filters the choices already loaded and does not call your endpoint again. For Dropdown and Multi-select, if the call fails, the agent can close and reopen the list to retry.
Nested levels: each level of a Nested API Dropdown calls its own endpoint: the first level when the field is shown, and each next level when the choice in the level before it changes. The admin passes that choice as a query parameter using a placeholder such as
{[Category]}, which is replaced with the selectedvalue. Until the previous level has a choice, the agent sees Please complete the previous field first.
Changing the source of a saved field
An admin can switch a saved Dropdown or Multi-select field between Manual and From API at any time. The builder does not ask for confirmation. After the form is saved, new submissions change as follows:
Dropdown:
valuechanges from the choice label to thevaluefrom your API, for example from"Shipping"to"CAT-14". If your system stores or matches the label, readselected_option.labelinstead.Multi-select:
valuekeeps the same shape, but eachvalueinside it changes from the label to thevaluefrom your API, for example from{ "label": "Price inquiry", "value": "Price inquiry" }to{ "label": "Price inquiry", "value": "TAG-104" }.Payload keys:
sourcechanges to"api",apiis added, anddropdownsis no longer sent.Switching back to Manual reverses all of the above.
Submissions that were already sent are not changed. Agents who have the form open while the change is saved see This form was just updated. Check your answers, then submit again. when they submit; their answers to unchanged fields are kept.
Webhook
Each submission is sent to the Webhook URL configured on the form's Submit Button.
Request
The form can define up to 5 custom headers. Use them to verify that a request comes from OmniForm.
Body
attachments, conversation_file, additional_info, customer_properties, and room_info are included only when the matching option is turned on in the form. The sample is shortened: each field's payload also carries that field's settings, such as its choice list or endpoint configuration.
Key | Description |
|---|---|
| The form's name and short description |
| The agent who submitted the form: |
| One object per field, in form order. See Field values by type |
| Files the agent attached |
| Link to the conversation history of the current session |
| The room's additional information. |
| The selected customer properties |
| The selected room details. Only the items ticked in the form are included |
Field values by type
Each field's answer is in fields[].payload.value:
Field type |
|
|
|---|---|---|
Single-line Text |
| Text |
Multi-line Text |
| Text |
Phone Number |
| Text with digits only, country code first, no |
Checkbox |
| A JSON-encoded string of the selected labels ( |
Dropdown |
| Text: the selected choice. With a From API source, the |
Nested API Dropdown |
| Array with one object per level: the level's |
Date and Time |
| ISO 8601 timestamp in UTC ( |
Number and Decimal |
| Number. The type is |
Multi-select |
| Array of the selected choices as |
For Checkbox and Dropdown,
payload.choices/payload.dropdownscontain only the selected choices.identifieris a stable key for each field. Map fields byidentifierrather thanlabel, because a label can be renamed.
Dropdown and Multi-select payload keys
Key | Fields | Description |
|---|---|---|
|
|
|
|
| Endpoint configuration: |
|
| The Choice Limit. Present only when set |
|
|
|
|
| The selected choices, in the same format as before |
Multi-select sends the same {label, value} shape for both sources. For a Manual source, each value equals its label.
Dropdown also sends payload.selected_option with both the label and the value of the selected choice. payload.value stays a plain string, so existing integrations keep working.
Responding to OmniForm
Respond with HTTP status 200 once you have accepted the submission. Any other status, including 201 or 204, is treated as a failure, and the agent sees that the submission failed.
To show your own error message to the agent, return one of these structures. If more than one is present, OmniForm uses the first match in this order: error.data.message, error.message, message.
Preferred:
Alternative 1:
Alternative 2:
messageis shown to the agent.detailsis optional and is not shown to the agent. Use it for debugging.
If none of these structures is returned, the agent sees:
When a submission fails
OmniForm sends each submission once and does not retry it automatically.
The agent sees the error message, and their answers stay in the form, so they can correct them and submit again. Each attempt is a new request to your webhook.
The agent waits for your response before seeing the result, so respond quickly.
If your service has already processed a submission but responds with a status other than 200, the agent will likely submit it again. Return 200 as soon as you accept the data, and make sure a repeated submission does not create a duplicate record.
Troubleshooting
The agent sees "unexpected error when sending data to webhook url …"
Your webhook responded with a status other than 200, or with a body OmniForm does not recognize. Check the status code in the message, and see Responding to OmniForm.
My webhook returns 201 but the agent sees an error
Only 200 counts as success. Change the status your webhook returns.
Choices from my API don't load
Check that the endpoint allows cross-origin requests (CORS) from the Omnichannel agent dashboard, and that it returns a plain JSON array of {label, value} objects. Use Test API in the builder to see the response.
The Checkbox value is a string, not an array
That is the Checkbox format. Parse payload.value as JSON to get the list of selected labels.
Dates are a few hours off
Date and Time values are sent in UTC. Convert them to your timezone.
Qiscus Technology