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.
The Response Submit API lets your server send responses straight into an Encatch form. Use it when a response was collected somewhere Encatch cannot show a form. That could be your own UI, a call-center tool, a kiosk, an import job, or another product's webhook.
Responses sent through the API appear in Results, reports, and destinations like any other response, usually within seconds.
There are two ways to send a response:
| Standard API | Custom API | |
|---|---|---|
| Body | The Encatch Standard body: status, answers keyed by question, and optional metadata | Any JSON your system already sends (for example a CRM or survey webhook) |
| Setup | None; turn the API on for the form | Create a format, then map payload fields onto the Standard body |
| URL | /v2/admin/forms/{formId}/responses | /v2/admin/forms/{formId}/formats/{slug}/responses |
| Best for | Code you control | Senders whose payload you cannot change |
Both use the same validation, idempotency, and error codes. A custom format is converted to the Standard body first and then checked the same way.
Turn on the API for a form
Open the form and go to Distribution → API.
Switch on API (Accept responses sent to the admin API for this form).
Click Save settings. Distribution settings apply to the live form as soon as you save.
Create an admin API key in the same project if you do not have one.
The API overview panel on the same page answers Is the API taking responses? for Standard and every custom format. It also shows the Base URL, the Form ID, and the form version that answers are checked against.
The form must be live
The API accepts responses only while the form is published, the API switch is on, the form is inside its schedule, it is under its response limit, and your organization has response quota left. Otherwise the call returns 422 with a code that names the reason.
Endpoints
All endpoints share the base URL https://api.encatch.com/engage-product/encatch/api and carry the form ID in the path.
| Endpoint | What it does |
|---|---|
POST /v2/admin/forms/{formId}/responses | Validates and stores a response (Standard body) |
POST /v2/admin/forms/{formId}/responses/validate | Checks the same body; nothing is stored |
POST /v2/admin/forms/{formId}/uploads | Uploads a file for a file upload, signature, or video and audio question, and returns a fileId |
POST /v2/admin/forms/{formId}/formats/{slug}/responses | Maps your JSON with a custom format and stores the response |
POST /v2/admin/forms/{formId}/formats/{slug}/responses/validate | Maps and checks your JSON; nothing is stored |
formId may also be sent in a JSON body. If you send it, it must match the path, or the call returns 400 form_id_mismatch.
Authentication
Send an admin (secret) API key in the X-Api-Key header, and call the API from your server only. Publishable SDK keys get 403.
-H 'X-Api-Key: YOUR_ADMIN_API_KEY'Some senders cannot set headers, such as another product's webhook. They can put the same key in the query string instead, as ?X-Api-Key=.... The gateway removes it before the request goes further.
Prefer the header
A key in the URL is stored in the sender's settings and in any log that records full URLs. The key also works for every form in the project. Use the query string only when the sender gives you no way to set a header, and use a dedicated key that you can rotate.
Admin keys are scoped to one project. See Admin API Keys and SDK & Admin API Keys.
Get this form's API documentation
Every form generates its own OpenAPI 3.1 document for its live version. The document covers every question's value schema with its options, limits, and an example. In Distribution → API → Standard API → Send a response:
- Try it: edit a body and click Validate to check it against the live form. Nothing is stored, and the usage quota is not checked. Copy as curl gives you the submit command with that body.
- OpenAPI: Preview docs, Copy JSON, or Download the document for Postman or a client generator.
- View questions: lists each question's slug, UUID, type, allowed values, and limits.
- View error codes: lists every error code the API can return.
Use the per-form document as the source of truth for the answer keys and option values of that form. The answer formats page explains the value shape of every question type.
Idempotency and retries
Send an Idempotency-Key header, or an externalId in the body, so that a retry never stores a second response.
- For 1 hour after the first accepted call, a repeat with the same body gets the same
202andresponseId, with the headerIdempotent-Replayed: true. Nothing new is stored. - A repeat with a different body gets
422 idempotency_conflict. - A repeat while the first call is still being queued gets
409 request_in_progresswith aRetry-Afterheader. - A request that failed validation or could not be queued holds no key. Retrying it with the same key processes it again.
This makes 503 temporarily_unavailable and network timeouts safe to retry with the same key.
Store payloads and logs
Each format has a Store payloads setting: Off, Failures only, or All. Standard has one, and each custom format has its own. When it is on, the requests are kept, encrypted, under Distribution → API → Logs. There you can inspect them by format and outcome (Success, Failed, Duplicate) and resend failed ones.
In production projects, logs are kept for up to 7 days for successes and 15 days for failures and duplicates. Store payloads is saved with the form settings, like the API switch.
With Store payloads off, new requests are not kept. Turn on Failures only while you build an integration, so that you can see rejected payloads and resend them after a fix.
Limits
| Limit | Value |
|---|---|
| JSON body size | 100 KB (413 body_too_large) |
submittedAt | Up to 15 days in the past, and no more than 2 minutes in the future |
| Idempotency window | 1 hour |
Idempotency-Key / externalId length | 1 to 255 characters |
| Uploaded file lifetime | 24 hours until it is used in a response, and one response per file |
| Custom formats per form | 20 |
| Mapping rules per custom format | 100 |
| Custom format sample payload | 32 KB |
| Rate limits | Admin API rate limits (429) |
Every response counts toward your organization's feedback responses allowance. Production keys get 422 usage_quota_exceeded when it is used up.
Good to know
- Responses are final.
status: "partial"only means the response is not complete. A response is never updated later, so send each response once, when you have all of its answers. - Required questions are not enforced. Leave a question out, or send
null, to skip it. - Unknown fields are rejected. A top-level field the API does not know returns
400 unsupported_field, so typos surface right away. In a custom format, payload fields that are not mapped are ignored. - Identified users must exist.
user.userNamemust be a user created with Identify User. Leaveuserout for an anonymous response. - Answers are checked against the live version. Publishing a new version changes which question keys and options are accepted.
Related
- Submit Response
- Answer formats
- Custom formats
- Error codes
- Build your own form UI with the Web SDK, for forms shown inside your app
Was this page helpful?
