Submit Form
Submits a lead from one of the account's forms. This writes a new contact into the account's CRM and notifies the site owner, so send only real visitor data, or clearly fictional data when you test.
/v1/forms/submitAuthentication
?key=pk_…Public key in the `key` query parameter. No OAuth, no secret. Optional: add the signed-in visitor's access token as `Authorization: Bearer` so the submission attaches to that visitor's existing contact (see `userId`). Browsers can call it only from the site's own origins. Server-side requests without an Origin header are accepted.
Edge cache: None (no-store)
Query Parameters
keystringThe site's public key (pk_…). It selects the account. Safe to ship to the browser.
Body Parameters
formIdstringThe id of the form, from `form._id` of Get Form By Spot or Get Form By ID. It must belong to the account of the key. The example is fictional, replace it with one of your form ids.
formDataobjectThe answers, as an object keyed by field `id` from the form `schema`. Values are checked against each field's type, required flag, options and limits. Unknown keys are dropped. The example values are fictional.
sourcestringA label for where the lead came from. It is saved as the contact's source and on the activity. The default is `website-form`.
website-formcontextDataobjectOptional context shown with the lead, such as `mlsId`, `propertyAddress` or `popupId`. Values are strings. The values you send are saved as is.
pageUrlstringURL of the page where the form was sent. Used for analytics.
pageTitlestringTitle of that page. Used for analytics.
utmSourcestringUTM source of the visit.
utmMediumstringUTM medium of the visit.
utmCampaignstringUTM campaign of the visit.
utmTermstringUTM term of the visit.
utmContentstringUTM content of the visit.
userIdstringContact id of a signed-in visitor. It is kept only when the `Authorization: Bearer` token belongs to that same contact, otherwise it is ignored. With a valid `userId` the name, email and phone fields become optional and the lead is added to that existing contact.
analyticsobjectOptional `{ visitorId, sessionId }`, two UUIDs, the same ones you send to the analytics endpoint. It attributes the `form_submission` event to the visitor.
Responses
application/jsontitlestringSuccess text. It is the same as `message`.
messagestringSuccess text you can show to the visitor.
hideToastbooleanAlways `false`. It is a hint for the website's own toast.
typestring`success` on a saved lead.
variantstring`default` on success, `destructive` on error.
samplebooleanOnly with a sample key: `true`, and nothing was saved.
Errors
400Every failure answers 400 with `type: "error"`, `message`, an inner `status` and, for validation failures, `errors` keyed by field id (for example `errors.field_1700000000003.message`). Causes: form not found or from another account (`Form not found`), a missing or invalid field (inner status 422, `Please check the form for errors`), no field mapped to name and email (`Form must include name and email fields`), a `formId` that is not a valid id, or a failure while saving or sending the owner's notification email.401Missing or invalid public key.403The account has no active subscription. The body also carries `code: "TENANT_INACTIVE"`.500The request body is not valid JSON, or an unexpected server error happened. The body is `{ "message": "Internal Server Error" }`.Notes
- This endpoint writes to the account's CRM. A successful call creates a contact (type lead, stage new-lead), adds a form activity and an incoming message, notifies the owner in the app and by email, and increments the form's submission count.
- It does not look for an existing contact with the same email. Submitting twice creates two contacts, unless `userId` points to an existing contact. There is no rate limit in the endpoint, and bot detection is observe-only, so add your own protection on the page (for example a honeypot or a captcha).
- Unless `userId` is valid, the form must contain a field mapped to name and a field mapped to email, and both must have a value. The notification email is part of the save: if it fails, nothing is saved and you get a 400.
- A sample key (`pk_..._s_...`, used for catalog template previews) answers the same success with `sample: true` and writes nothing.
curl --request POST \--url "https://api.pandaidx.com/v1/forms/submit?key={{public_key}}" \--header "Content-Type: application/json" \--data '{"formId":"6a1f3c9d2b7e4a0012ab34cd","formData":{"field_1700000000001":"Alex Example","field_1700000000003":"alex.example@example.com"}}'
{"title": "Thank you! We'll be in touch soon.","message": "Thank you! We'll be in touch soon.","hideToast": false,"type": "success","variant": "default"}