Submit Response
Store a form response from your backend with POST /v2/admin/forms/{formId}/responses, using the Standard body.
Validate a response against the form's live version, and store it if it passes. The response appears in Results within seconds. It also reaches your reports and destinations like a response from any other channel.
Turn on Distribution → API for the form first (see Response Submit API). Authenticate with an admin API key. Admin API rate limits apply.
To check a body without storing it, send the same body to Validate Response.
Authentication
Admin (secret) API key. A sender that cannot set headers can pass it as the X-Api-Key query parameter instead; see Authentication.
Request body format
1 to 255 characters, unique per response. A repeat within 1 hour returns the first call's responseId with Idempotent-Replayed: true. A repeat with a different body gets 422, and a repeat while the first call is still queuing gets 409. See Idempotency and retries.
Publishable SDK keys receive 403 (publishable API keys cannot call /v2/admin routes).
Server-side only
Keep admin API keys on your server. Never ship them in client code.
Path parameters
The form's UUID. Copy it from Distribution → API → API overview → Form ID.
Request body
Maximum body size: 100 KB (413 if larger). Only status and answers are required. Any top-level field not listed here returns 400 unsupported_field.
partial only means the response is not complete. A response is never updated later.
Answers keyed by question slug or question UUID. The value shape depends on the question type; see Answer formats. To skip a question, leave it out or send null. Required questions are not enforced. Answering the same question by both slug and UUID returns duplicate_answer.
Optional. Must match the formId in the path.
Your own id for this response or event, 1 to 255 characters. It works like Idempotency-Key: a repeat within 1 hour returns the same responseId, and a different body gets 422 idempotency_conflict.
When the response was given: ISO 8601 with a time zone, for example 2026-10-19T07:06:00Z. It can be up to 15 days in the past and no more than 2 minutes in the future. Defaults to now.
One of the form's languages, for example en. Defaults to the form's default language.
The respondent. Leave it out for an anonymous response.
An identified user that already exists; create one with Identify User. The contact variables the form uses are filled from this user's traits, as they are with the SDK. Contact values themselves cannot be sent.
Values for the form's context variables, keyed by variable name.
Values for the form's source tracking parameters. A key that is not one of the form's parameters, or any key while source tracking is off, returns unknown_source_tracking_param.
deviceobjectOptional device metadata. Every key is a string or null. Unknown keys return unsupported_field.
Show 14 propertiesHide 14 properties
Optional device metadata. Every key is a string or null. Unknown keys return unsupported_field.
IANA time zone, for example Europe/London
Operating system
Operating system version
Application identifier
Application version
Device locale, for example en-GB
User language, for example en
URL or screen name the response came from
Device type, for example desktop
Viewport class
Color scheme
Browser name
Browser version
ISO 3166-1 alpha-2 country code, for example GB
The respondent's IPv4 or IPv6 address.
Whole seconds the respondent took, from 0 to 2147483647.
Milliseconds spent on each question, keyed like answers (by slug or UUID). This field never rejects the request. Decimals are rounded to whole milliseconds, text that holds a number is read as that number, and values over 24 hours (86,400,000) are capped at 24 hours. 0, negative numbers, and anything else record no time.
Keys that are not a question, or that point to an unanswered question, are ignored, so a mistyped key loses its time without an error. When one question is given by both slug and UUID, the UUID entry is used.
AI topics for text answers, on completed responses only. Defaults to true.
Examples
curl -X POST 'https://api.encatch.com/engage-product/encatch/api/v2/admin/forms/3f8a2c1e-7b4d-4e9a-9c61-2d5f8e0b7a14/responses' \
-H 'X-Api-Key: YOUR_ADMIN_API_KEY' \
-H 'Content-Type: application/json' \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"status": "completed",
"externalId": "order-48213",
"answers": {
"how-likely-are-you-to-recommend-us": 9,
"what-did-you-like-most": ["fast-delivery", "easy-checkout"],
"anything-we-could-improve": "Tracking updates could be more frequent."
}
}'{
"status": "completed",
"externalId": "order-48213",
"submittedAt": "2026-10-19T07:06:00Z",
"language": "en",
"user": { "userName": "jane.doe@example.com" },
"context": { "accountTier": "pro" },
"device": {
"timezone": "Europe/London",
"deviceType": "desktop",
"deviceOs": "macOS",
"browser": "Chrome",
"deviceLanguage": "en-GB",
"countryCode": "GB"
},
"ipAddress": "203.0.113.7",
"completionTimeInSeconds": 95,
"aiEnrichment": true,
"answers": {
"how-likely-are-you-to-recommend-us": 9,
"what-did-you-like-most": {
"values": ["fast-delivery", "$Other"],
"otherText": "The packaging"
},
"anything-we-could-improve": "Tracking updates could be more frequent."
},
"timeSpentMs": {
"how-likely-are-you-to-recommend-us": 4200,
"what-did-you-like-most": 12500,
"anything-we-could-improve": 31000
}
}The example keys (how-likely-are-you-to-recommend-us and so on) are question slugs. Take the real slugs, UUIDs, and option values from Distribution → API → View questions or from the form's OpenAPI document.
Success response
{
"responseId": "7d0f3b52-9a1e-4c8d-b6f2-5e4a3c2b1d09",
"requestId": "4b1e0c7a92f3d811"
}UUID of the stored response. A replayed request returns the first call's responseId.
Correlation id for this call. Quote it when contacting support.
202 Accepted means the response is queued. It shows in Results within seconds. When the call answers a repeat (same Idempotency-Key or externalId, same body), the response carries the header Idempotent-Replayed: true and nothing new is stored.
Errors
Failed calls return application/problem+json (RFC 9457) with a stable code. When the body has more than one problem, code is validation_failed, and every problem is listed in invalidParams with its own code. Match on code, not on detail or message. Treat a code you do not know like any other error with that status. The full list is on Error codes.
{
"type": "about:blank",
"title": "Unprocessable Entity",
"status": 422,
"code": "out_of_range",
"detail": "Must be between 0 and 10.",
"requestId": "4b1e0c7a92f3d811",
"invalidParams": [
{
"name": "answers.how-likely-are-you-to-recommend-us",
"code": "out_of_range",
"message": "Must be between 0 and 10."
}
]
}| Status | When |
|---|---|
400 | Body is not valid JSON or a field has the wrong type (malformed_body), unknown top-level field (unsupported_field), formId in the body differs from the path (form_id_mismatch), or Idempotency-Key is not 1 to 255 characters (invalid_idempotency_key) |
401 | Missing or unknown API key; answered by the gateway as { "error": "..." }, without a code |
403 | The key is a publishable SDK key, not an admin key; answered by the gateway, without a code |
404 | No form with this formId in the key's project (form_not_found) |
409 | The first request with this Idempotency-Key or externalId is still being queued (request_in_progress); retry after Retry-After seconds |
413 | Body larger than 100 KB (body_too_large) |
422 | The form cannot take a response now (form_not_live, api_channel_disabled, outside_schedule, response_limit_reached, usage_quota_exceeded), the body has problems (see Error codes), or the same key was used in the last hour with a different body (idempotency_conflict) |
429 | Admin API rate limit reached |
503 | The queue or file storage is temporarily unavailable (temporarily_unavailable); safe to retry with the same Idempotency-Key |
{
"error": "api key is required"
}Related
- Response Submit API overview
- Validate Response
- Upload File, for file upload, signature, and video and audio questions
- Answer formats
- Custom formats, to send a payload you cannot change
Was this page helpful?
Response Submit API
Send form responses from your own backend to an Encatch form, in the Standard body or in a custom format you map from any JSON.
POSTValidate Response
Check a response body against a form's live version without storing it, with POST /v2/admin/forms/{formId}/responses/validate.
