Welcome to Encatch Docs
Admin API ReferenceResponses

Custom formats

Accept the JSON another system already sends, such as a CRM or survey webhook, by mapping its fields onto the Standard body with a custom format.

A custom format is an incoming URL for a system that cannot send the Standard body. It takes the JSON that system already sends, for example a CRM, helpdesk, or survey webhook. It maps the payload's fields onto the Standard body and stores the response.

Each custom format has its own URL, field mapping, and Store payloads setting. A form can have up to 20 custom formats.

POSThttps://api.encatch.com/engage-product/encatch/api/v2/admin/forms/%7BformId%7D/formats/%7Bslug%7D/responses

After mapping, the request goes through the same checks, idempotency, and error codes as Standard. A few extra codes cover mapping problems.

Create a format

Open the form and go to Distribution → API → Custom API, then click New format. Make sure the API switch is on and saved.

Enter a Name (up to 120 characters) and a Slug. The slug is part of the URL and cannot change later. It must be unique on the form, up to 100 characters, and use lowercase letters and numbers separated by single hyphens or underscores, for example crm-nps-webhook. standard and validate are reserved.

Choose Store payloads (Off, Failures only, or All), and optionally add a Description (up to 500 characters).

Paste a Sample payload: one real request exactly as your system sends it, up to 32 KB. The editor offers paths from it, and the sample is saved with the format.

Build the Field mapping (below), check it with Preview with the sample, and click Create format.

The format card shows its URL, how many fields are mapped, and its Store payloads setting. From the card menu you can Edit details, Duplicate, Copy URL, or Delete the format. Use the pencil icon to edit the mapping.

Field mapping

Each mapping row fills one field of the Standard body. Add field lists every field you can fill:

GroupTargets
AnswersEvery question on the live version, shown with the value shape it takes
Other textThe text for the Other option of choice questions that have one
Time spentMilliseconds spent on each question (timeSpentMs)
Responsestatus, externalId, submittedAt, language, user.userName, ipAddress, completionTimeInSeconds, aiEnrichment
Devicetimezone, deviceOs, deviceOsVersion, app, appVersion, deviceLanguage, userLanguage, urlOrScreenName, deviceType, deviceSize, preferredTheme, browser, browserVersion, countryCode
Context variablesThe form's context variables (add them in Form design)
Source trackingThe form's source tracking parameters (turn on source tracking first)

A format has 1 to 100 rows, and each target can be mapped only once. Payload fields that are not mapped are ignored. status is completed unless you map it.

Row settings

Open a row's settings (the sliders icon) to choose where its value comes from and how it is converted.

ValuestringRequired

From payload reads a JSONPath from each request, for example $.survey.score. Paths start with $, and the picker suggests paths found in the sample. Fixed value sends the same value on every request, for example "partial" for status.

From payloadFixed value
Convertstring

How the payload value is turned into the field's type. See Conversions.

As isTextNumberTrue / falseDateListDuration
Defaultany

Used when the path matches nothing in the request. Leave it empty to leave the field out. Defaults apply only to From payload rows.

Option maparray

Single choice, multiple choice, picture choice, and ranking answers only. Each entry maps an incoming value to one of the question's options, with up to 200 entries, and each incoming value can be mapped once. When the map is empty, incoming values are sent as option values as they are. When the map has entries, a value with no entry returns option_not_mapped.

When a path matches several values, they are collected into an array for multiple choice answers and lists. For any other field, several matches return path_matched_many.

Conversions

ConvertWhat it doesExtra setting
As isSends the payload value unchanged—
TextConverts to a string—
NumberConverts to a number, for example "9" → 9—
True / falseConverts to a boolean—
DateParses a date and timeReads: ISO 8601, Unix seconds, Unix milliseconds, or RFC 2822
ListSplits text into an array, for example "Delivery;Checkout" → ["Delivery", "Checkout"]Split text on: the separator, , by default
DurationTime spent fields only. Reads numbers in the chosen unit, and text such as 1m 30s, 00:01:30, or PT1M30SNumbers are in: milliseconds, seconds, or minutes

A value that cannot be converted returns conversion_failed.

Check a mapping

The editor has three panes under the sample. None of them stores a response.

  • Preview: Preview with the sample runs the draft mapping in the API and shows whether the result is valid, or lists each problem with its code.
  • Test: paste a body of your own and check it against the mapping in the editor, saved or not, with the same engine the API uses.
  • Requests: ready-to-run curl commands for this format's submit and validate URLs, with the sample as the body. They use the saved mapping, not unsaved edits.

Endpoints

EndpointWhat it does
POST /v2/admin/forms/{formId}/formats/{slug}/responsesMaps the payload, validates it, and stores the response
POST /v2/admin/forms/{formId}/formats/{slug}/responses/validateMaps and validates the payload; nothing is stored

Both take any JSON body up to 100 KB, and both authenticate with an admin API key:

  • Send the key in the X-Api-Key header when the sender lets you add headers.
  • Otherwise, append ?X-Api-Key=YOUR_ADMIN_API_KEY to the URL. See Authentication for the trade-offs.
  • Send an Idempotency-Key header, or map externalId from the payload's event id, so retries from the sender are de-duplicated.

The submit URL answers the same way as Submit Response, with 202 and a responseId. The validate URL answers the same way as Validate Response.

Example: a CRM NPS webhook

Say a CRM posts this payload after each NPS survey, and you cannot change it:

cURL
curl -X POST 'https://api.encatch.com/engage-product/encatch/api/v2/admin/forms/3f8a2c1e-7b4d-4e9a-9c61-2d5f8e0b7a14/formats/crm-nps-webhook/responses' \
  -H 'X-Api-Key: YOUR_ADMIN_API_KEY' \
  -H 'Content-Type: application/json' \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "event_id": "evt_1001",
    "occurred_at": 1792393591,
    "contact": { "email": "jane.doe@example.com" },
    "survey": {
      "score": "9",
      "liked": "Delivery;Checkout",
      "comment": "Tracking updates could be more frequent."
    }
  }'

Create a format with the slug crm-nps-webhook and these rows:

TargetValueConvertOption map
externalId$.event_idAs is—
submittedAt$.occurred_atDate, reads Unix seconds—
user.userName$.contact.emailAs is—
how-likely-are-you-to-recommend-us$.survey.scoreNumber—
what-did-you-like-most$.survey.likedList, split on ;Delivery → fast-delivery, Checkout → easy-checkout
anything-we-could-improve$.survey.commentText—

The API turns the payload into this Standard body, then validates and stores it:

JSONMapped Standard body
{
  "status": "completed",
  "externalId": "evt_1001",
  "submittedAt": "2026-10-19T07:06:31Z",
  "user": { "userName": "jane.doe@example.com" },
  "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."
  }
}

Because externalId comes from event_id, a webhook retried within 1 hour with the same payload returns the same responseId instead of storing a second response. user.userName must belong to a user created with Identify User. Otherwise, leave that row out to store anonymous responses.

Errors

Custom formats return the same codes as Standard, plus these:

CodeStatusMeaning
format_not_found404No custom format with this slug on the form
path_matched_many422A JSONPath rule matched several values where the field takes one
conversion_failed422The payload value could not be converted to the field's type
option_not_mapped422The payload value has no entry in the rule's option map
question_not_in_form422A rule targets a question the live version does not have

In invalidParams, name points at the rule's target field. The most common cause of question_not_in_form is publishing a new form version that removed or replaced a question. Re-open the format after publishing, and remap or remove the affected rows.

Was this page helpful?