Submit entries from your server
Create and submit an entry in one request, handle validation errors, and control the emails and PDFs that follow.
This page shows how your server submits an entry without anyone opening the form, for example to record data your system already holds, or to let a partner's system send in a form. You will submit an entry, handle the errors the form's rules return, and control the emails and PDFs the submit triggers.
Submit an entry
POST /entry/submit creates an entry and submits it in
one request. The body holds the form id and the entry's data:
curl -X POST https://api.tepez.co.il/v1.0/entry/submit \
-H "x-api-key: $TEPEZ_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"form": "<formId>",
"data": {
"fullName": "Dana Levi",
"email": "dana@example.com",
"position": "dev",
"address": {"city": "Haifa", "street": "Herzl 10"}
}
}'The response, trimmed:
{
"entry": {
"id": "<entryId>",
"form": "<formId>",
"status": "submitted",
"idx": 1,
"submitIdx": 1,
"data": {
"fullName": "Dana Levi",
"email": "dana@example.com",
"position": "dev",
"address": { "city": "Haifa", "street": "Herzl 10" }
},
"actions": [
{ "id": "<actionId>", "type": "submit", "time": "2026-09-30T19:45:59.995Z", "ip": "<ip>", "userAgent": "curl/8.16.0", "user": "<userId>" }
],
"sentEmails": [],
"archivedRenders": [
{
"id": "<renderId>",
"action": "<actionId>",
"type": "template",
"template": { "type": "auto" },
"file": {
"name": "Job application.pdf",
"size": 24959,
"type": "application/pdf",
"s3Key": "<formId>/<entryId>/<renderId>/Job application.pdf",
"s3Bucket": "<bucket>",
"url": "https://<bucket>.s3.eu-west-1.amazonaws.com/<formId>/<entryId>/<renderId>/Job%20application.pdf?<presigned-query>"
}
}
],
"submitTime": "2026-09-30T19:45:59.995Z",
"createTime": "2026-09-30T19:46:00.002Z",
"modifyTime": "2026-09-30T19:46:00.002Z",
"changedAt": "2026-09-30T19:46:00.646Z"
},
"uiActions": [{ "type": "message", "text": "The form was submitted successfully." }],
"entryId": "<entryId>"
}Store entry.id next to your own record, so you can read, edit or delete the entry later. The
Tepez entry id is the only way to find it again: the API can't search entries by their data.
What goes in data
- Send the whole entry. A submit stores exactly the
datayou send. The same holds forPOST /entry/{entryId}/submit, which submits an existing draft: itsdatareplaces the draft's data, so send every value, not only the new ones. - Use the form's paths and value shapes. Each value sits at the path of its field, in the shape the field expects. Form data schema explains both, and how to list a form's fields.
- Keys the form doesn't declare are dropped without an error, so check the stored
datain the response while you build the integration. - Files are uploaded first. A file field takes file objects, which you get by uploading the file. Upload a file shows the three steps.
Send computed values yourself
Some values in a form are filled in by the form itself as the person works: a field's initial value
(initialValue), or a calculated element (eEq) that fills a field from other fields. These run in
the person's browser only. A submit through the API runs none of them, so a field whose value the
form would calculate stays empty unless you send its value.
The form in this page's examples has a field source, "How did you hear about us?", with the
initial value website. In the browser the field starts as website. The entry submitted above
has no source at all. If the value matters, compute it in your system and send it in data.
With or without an API key
POST /entry/submit also works without a key. It is the request the form's public fill page
sends, so anyone who has the form's id can submit an entry to it:
curl -X POST https://api.tepez.co.il/v1.0/entry/submit \
-H "Content-Type: application/json" \
-d '{"form": "<formId>", "data": {"fullName": "Yossi Cohen", "email": "yossi@example.com", "position": "qa"}}'The response is the same, except that the submit action has no user:
"actions": [
{ "id": "<actionId>", "type": "submit", "time": "2026-09-30T19:46:10.168Z", "ip": "<ip>", "userAgent": "curl/8.16.0" }
]Which to use:
- From your own server, send your key. The action then records your integration user, so you can tell the entries your system submitted from the ones people filled in.
- For a partner's system, give them the form id, not your key. A key can read and change every entry of your account (What a key can do), while a submit without a key can only add entries to that form. The form's rules check each entry the same way. Security lists what else works without a key.
Handle validation errors
The form's rules check every submit. When the data breaks one, nothing is saved, and the
response is 400 with id set to entry_failed_validation and one item in messages per
problem. This submit leaves out the required fullName and sends two wrong values:
curl -X POST https://api.tepez.co.il/v1.0/entry/submit \
-H "x-api-key: $TEPEZ_API_KEY" \
-H "Content-Type: application/json" \
-d '{"form": "<formId>", "data": {"email": "not-an-email", "position": "manager"}}'{
"statusCode": 400,
"error": "Bad Request",
"message": "The entry failed validation on the server. Sorry, this is probably a BUG on our side. If the issue persists please contact us at support@tepez.co.il",
"id": "entry_failed_validation",
"messages": [
{ "modelPath": "model['fullName']", "message": "You can't leave this empty.", "type": "error", "value": null },
{ "modelPath": "model['position']", "message": "not in options", "type": "error", "value": "manager" },
{ "modelPath": "model['email']", "message": "Must be a valid email address.", "type": "error", "value": "not-an-email" }
]
}Each item of messages names the field in modelPath, says what is wrong in message, and
echoes the value you sent. Despite the wording of the top-level message, this almost always
means the data broke a rule of the form, not a fault on our side. Fix the fields named in
messages and submit again.
The texts in messages are in the form's language, as a person filling the form would see them,
whatever locale the request asks for.
Check the HTTP status, not the presence of id: a successful response has no top-level id, and
the id of a failed one is the error code. Errors describes this
error and the other 400 shape.
What runs after the submit
A submit from your server triggers the form's after-submit rules, exactly as a submit from the browser does. Before the response returns:
- the form archives its PDFs, which the response lists in
entry.archivedRenders(The archived PDF) - the form sends its emails, which the response lists in
entry.sentEmails uiActionsholds what the browser would show next, such as a thank-you message or a redirect. Your server can ignore it.
Two options in the body change this for one request.
Test the emails
testEmailAddress sends every email of the request to that one address instead of its
recipients. Use it while you build the integration, to see the emails without reaching the real
recipients. This form emails HR on each submission:
curl -X POST https://api.tepez.co.il/v1.0/entry/submit \
-H "x-api-key: $TEPEZ_API_KEY" \
-H "Content-Type: application/json" \
-d '{"form": "<formId>", "data": {"fullName": "Rina Katz", "position": "qa"}, "testEmailAddress": "you@example.com"}'The entry's sentEmails:
"sentEmails": [
{
"id": "<emailId>",
"action": "<actionId>",
"from": { "name": "Tepez", "address": "noreply@tepez.co.il" },
"to": ["hr@example.com"],
"replyTo": ["hr@example.com"],
"testEmailAddress": ["you@example.com"],
"subject": "New application: Rina Katz",
"text": "Rina Katz applied for qa."
}
]to records the rule's recipients, and testEmailAddress the address the email actually went to.
The entry itself is real: it is submitted, and its PDF is archived. Delete it when you finish
testing.
Import without emails or PDFs
To load past entries into Tepez, for example records from a system you are replacing, you usually
don't want an email or a PDF for each one. skipEmails and skipRenders turn them off for the
request, and skipUiActions returns an empty uiActions:
curl -X POST https://api.tepez.co.il/v1.0/entry/submit \
-H "x-api-key: $TEPEZ_API_KEY" \
-H "Content-Type: application/json" \
-d '{"form": "<formId>", "data": {"fullName": "Avi Mor", "position": "dev"}, "skipEmails": true, "skipRenders": true, "skipUiActions": true}'The response, trimmed:
{
"entry": {
"id": "<entryId>",
"status": "submitted",
"sentEmails": [],
"archivedRenders": []
},
"uiActions": []
}The entry is submitted like any other, with no email sent and no PDF archived. To get a PDF of it later, render one. The same flags work on edits: see Update without emails or PDFs.
If the request times out
A submit that archives PDFs and sends emails can take a few seconds. If your request times out,
or you get a 504 with no JSON body, the entry may still have been stored. Before you submit
again, list the form's entries that changed in the last few minutes, with
GET /entry and a changedAt filter, and check
whether yours is there. Otherwise a retry can create the entry twice. See also
Timeouts and gateway errors.
Troubleshooting
| Response | Cause and fix |
|---|---|
400 entry_failed_validation | The data broke a rule of the form. Fix the fields named in messages. Form data schema shows the value each field takes. |
400 "form" is required or "data" is required | The body lacks the form id or data. Send both. |
404 Could not find a form with ID … | The form id is wrong. Copy it from the form's link or from GET /form. |
200, but a value is missing from the stored data | The key isn't a field of the form, or its path is wrong, so it was dropped. Check Data paths. |
200, but a field the form calculates is empty | Calculated values and initial values run only in the browser. Send the value in data. |
403 You cannot submit this form entry because it was already submitted. | POST /entry/{entryId}/submit on a submitted entry. To change it, edit it. |
500 on a submit with a file | A file object points at a file that doesn't exist in storage. Upload the file first. |
504, or the request timed out | The entry may have been stored. Check before you retry, as described above. |
| The form's emails reached real people during a test | The test submit had no testEmailAddress. Add it to every test request. |
Errors explains the error format and lists more messages.
Every response from the API carries an x-tepez-request-id header. Quote it when you contact us.
Question about this page? Email support@tepez.co.il