https://api.[tenant].getomneo.com/api/v3.
Submitting a fill
Post the fill to the questionnaire’s submissions endpoint:201.
| Field | Type | Notes |
|---|---|---|
profile_id* | uuid | The Profile who filled the form. |
answerable* | object | What the answers are about. See below. |
answers* | array | One entry per answer. See Addressing each answer. |
context | object | Optional resolution hints used when routing needs to find a target, for example transaction_id, transaction_item_id, appointment_id, or connection_id. |
The answerable
answerable is the subject of the fill: { "type": ..., "id": ... }. The type names what the answers describe and id identifies the specific record. Common types are profile, transaction, product_variant, and appointment, and many other resource types are accepted.
Addressing each answer
Each entry inanswers carries a value and identifies which question slot it belongs to. You can identify the slot three ways, in order of specificity:
| Identifier | Use when |
|---|---|
questionnaire_question_id | You have the exact slot id from the questionnaire. Most precise. |
question_id | You have the question id but not the slot id. |
question_handle | You are working from stable handles, for example a hand-authored front end. |
context.transaction_id tells any transaction-routed answer which transaction to write to. Supply the context key a question needs to resolve its destination: transaction_id, transaction_item_id, appointment_id, or connection_id.
What happens after submission
Routing runs after the submission is saved. Each answer is routed independently against the link configuration on its question, and one answer failing does not affect the others. An answer is saved aspending, then routing moves it to one of three terminal states:
| State | Meaning |
|---|---|
pending | Saved but not yet routed. Re-read the submission or answer to see the terminal state once routing has run. |
mapped | The destination was written. |
skipped | Nothing to write, for example a capture-only question (link_type of none). |
failed | The write could not be applied, for example the target could not be resolved. |
link_type writes and the profile write policies, see Routing answers.
The submission response
The201 response is the observable result of the fill. Its answers[] is not an echo of what you sent: each entry is the routing outcome for one answer, carrying the resolved destination and a mapping_status. This example fills the post-visit form with a score that routes to a Rating and a comment that is captured only:
Top-level fields
| Field | Notes |
|---|---|
submission_id | Id of the saved submission. |
questionnaire_id | The questionnaire that was filled. |
submitted_count | Number of answers accepted. |
answerable | The subject of the fill, as { type, id }. |
context | The resolution hints supplied on the request. |
submitted_at | When the fill was recorded. |
answers | One routing outcome per answer. See below. |
Answer outcome fields
Each entry inanswers[] is a routing outcome:
| Field | Notes |
|---|---|
id | The stored answer id. |
questionnaire_question_id | The slot the answer was given for. |
question_id | The question id. |
question_handle | The question handle. |
question_version_id | The question version the answer is pinned to. |
profile_id | The Profile who answered. |
answerable_type | Type of the submission subject. |
answerable_id | Id of the submission subject. |
mapping_key | The slot’s key within the form. |
link_type | Routing destination kind, copied from the question. |
link_target | Destination within the link_type. |
link_write_policy | Overwrite policy applied. |
question_label | The label shown at submission time. |
question_type | The input type. |
question_required | Whether the slot was required. |
answer_type | Data type of the stored value. |
value | The submitted value. |
mappable_type | Type of the record written. Populated when mapping_status is mapped. |
mappable_id | Id of the record written. Populated when mapping_status is mapped. |
mapped_field | Field written on the destination. Populated when mapping_status is mapped. |
mapping_status | mapped, skipped, or failed. See below. |
mapping_error | Reason the write could not be applied. Populated when mapping_status is failed. |
mapped_at | When the write happened. Populated when mapping_status is mapped. |
meta | Free-form metadata. |
created_at | When the answer was stored. |
updated_at | When the answer last changed. |
mapping_status:
mapping_status | Meaning | Populated fields |
|---|---|---|
pending | The answer is saved but has not been routed yet. | None yet. |
mapped | The destination was written. | mappable_type, mappable_id, mapped_field, mapped_at. |
skipped | Nothing to write, for example a capture-only question (link_type of none). | None of the mapping fields. |
failed | The write could not be applied. | mapping_error. |
A saved submission returns
201 even when some answers end in failed. Routing outcomes are reported per answer inside answers[], not as an HTTP error. Read each mapping_status to confirm the writes you expected actually landed.Routing to a Rating
An answer whose question haslink_type of rating always inserts a new Rating rather than updating an earlier one. Re-submitting a score for the same subject creates a second Rating, so the store accumulates every score over time.
When you need a single current value, such as a product’s latest NPS from one customer, read the Ratings for that subject and aggregate on the most recent. Do not assume one Rating per subject per customer.
End-to-end example
This walks a single post-visit form from authoring to inspecting its routing outcomes. Authoring detail lives in Building questionnaires; the commands here are condensed to show the whole flow.Build the questionnaire with a page
Create the form, add a page, then place both slots on that page.
Inspect the routing outcomes
Read
answers[] in the 201 response. The nps-score answer comes back with mapping_status of mapped and a mappable_type of rating, so a new Rating was written. The visit-comment answer comes back skipped because its link_type is none, so it was captured without a destination. This is the response shown in The submission response.Edge cases and gotchas
- The answerable frames the fill, answers still route independently.
answerablenames the overall subject, but each answer routes to the destination on its own question, which can differ from the answerable. A profile fill can still write a Rating and a Transaction answer at once. - Version pinning is permanent. Each stored answer records
question_version_id. Editing the question later creates a new version and never rewrites answers already captured, so historical responses stay exact. - Reads nest the rendering fields. When you read a question to render a form, its
label,type,options, andvalidationcome back undercurrent_version, not at the top level. See The question response shape. - Ratings always insert. A
ratinganswer creates a new Rating every time. Re-submitting a score for the same subject accumulates Ratings rather than updating one, so aggregate on the most recent when you need a single current value. - A
failedanswer does not fail the request. The submission is still saved and returns201. The failure is reported on that answer, withmapping_statusoffailedand a reason inmapping_error:
context key the question needs (transaction_id, transaction_item_id, appointment_id, or connection_id) so the destination can be found.
The booking case
Appointment booking questions are a questionnaire attached to the appointment definition, but their answers are not submitted through the submissions endpoint. Instead, send them inside the appointment create payload, on theanswers array, addressed by questionnaire_question_id:
Errors
| Status | When |
|---|---|
401 | The bearer token is missing or invalid. |
403 | The token is valid but lacks the scope or permission to submit. |
404 | The questionnaire does not exist, or a referenced slot is not part of it. |
422 | Validation failed. A submission requires profile_id, answerable, and answers. |
failed routing outcome is not an HTTP error. The submission still returns 201; inspect each answer’s mapping_status to confirm the writes landed.