Entries and their lifecycle
Entry statuses, how save, submit and edit differ, how to update an entry's data, templates, and an entry's history.
An entry (רשומה) is one filling of a form. This page explains the statuses an entry moves through, which requests move it, and what each request checks and triggers. It then shows how to change an entry's data safely, how template entries and pre-issued entries work, and where to find an entry's history.
Statuses
Every entry has a status. Responses give it as a label. Filters in query take its number:
Number (in query) | Label (in responses) | Meaning |
|---|---|---|
0 | created | An empty entry created with POST /form/{formId}/generateEntries. Nobody has saved it yet. |
1 | saved | A draft. The person, or your system, can still change and submit it. |
2 | submitted | Final. The person can't change it anymore. |
3 | deleted | The entry was deleted. List requests don't return deleted entries. |
4 | template | A template entry: a prefilled starting point for new entries. See Template entries. |
5 | render | A copy of the data behind a PDF render. Only forms set to keep renders create these. See Files and PDFs. |
To filter by status, pass the number, alone or with an operator such as $in. This lists the
empty and template entries of a form:
curl -G https://api.tepez.co.il/v1.0/entry \
-H "x-api-key: $TEPEZ_API_KEY" \
--data-urlencode 'query={"form":"<formId>","status":{"$in":[0,4]}}' \
--data-urlencode 'fields=["id","status"]' \
--data-urlencode 'limit=10'{
"data": [
{ "id": "<templateEntryId>", "status": "template" },
{ "id": "<entryId>", "status": "created" }
]
}A label in query, such as "status":"submitted", fails with
400 "query.status" must be one of [object, 0, 1, 2, 3, 4, 5].
Query and export entries lists every filter query
accepts.
Save, submit and edit
Three kinds of request write an entry's data. They differ in the status they leave the entry in, in what they check, and in which of the form's rules they trigger afterwards: the emails the form sends and the PDFs it archives.
| Request | Resulting status | Checks the form's rules | Rules that run afterwards |
|---|---|---|---|
POST /entry/save, POST /entry/{entryId}/save | saved | Only if the form is set to check drafts (formSchema.validateSaved). Values of the wrong type, such as a malformed date, always fail. | The form's after-save rules |
POST /entry/submit, POST /entry/{entryId}/submit | submitted | Always, all of them | The form's after-submit rules |
POST /entry/{entryId}/edit | Unchanged, or the status you send | When you send data and the result is submitted, or the result is saved and the form checks drafts. A status-only edit checks nothing. | The form's after-edit rules only |
The request without an {entryId} creates a new entry. The request with one continues an existing
created or saved entry. On a submit, the data you send replaces the entry's data entirely.
A submitted entry can't be saved or submitted again. Only an edit can change it.
Each of these requests can turn off the rules that run afterwards, for that request only, with
skipEmails and skipRenders, or send the emails to a test address with testEmailAddress. See
What runs after the submit.
Save a draft
curl -X POST https://api.tepez.co.il/v1.0/entry/save \
-H "x-api-key: $TEPEZ_API_KEY" \
-H "Content-Type: application/json" \
-d '{"form": "<formId>", "data": {"fullName": "Noa Peretz"}}'{
"entry": {
"id": "<entryId>",
"form": "<formId>",
"account": "<accountId>",
"status": "saved",
"idx": 7,
"data": { "fullName": "Noa Peretz" },
"actions": [
{
"id": "<actionId>",
"type": "save",
"time": "2026-09-30T16:02:44.837Z",
"ip": "<ip>",
"userAgent": "curl/8.16.0",
"user": "<userId>"
}
],
"sentEmails": [],
"archivedRenders": [],
"deleted": false,
"bewit": "<bewit>",
"resumeUrlBewit": "https://app.tepez.co.il/e/<entryId>/?b=<bewit>",
"resumeUrl": "https://app.tepez.co.il/e/<entryId>/",
"cloneUrl": "https://app.tepez.co.il/c/<entryId>/",
"editUrl": "https://app.tepez.co.il/admin/entries/<formId>/<entryId>/",
"createTime": "2026-09-30T16:02:44.843Z",
"modifyTime": "2026-09-30T16:02:44.843Z",
"changedAt": "2026-09-30T16:02:44.865Z"
},
"uiActions": [{ "type": "continueLater" }],
"userCanResume": true,
"formId": "<formId>",
"entryId": "<entryId>",
"bewit": "<bewit>"
}The new entry is under entry. resumeUrlBewit is the link that lets a person continue this
draft without signing in (see Authentication).
uiActions is what the form would show the person next. A server integration can ignore it.
A successful response has no top-level id. A failed check does: "id": "entry_failed_validation"
is an error code. Check the HTTP status, not the presence of id.
Submit it
curl -X POST https://api.tepez.co.il/v1.0/entry/<entryId>/submit \
-H "x-api-key: $TEPEZ_API_KEY" \
-H "Content-Type: application/json" \
-d '{"data": {"fullName": "Noa Peretz", "position": "qa"}}'{
"entry": {
"id": "<entryId>",
"status": "submitted",
"idx": 7,
"submitIdx": 4,
"data": { "fullName": "Noa Peretz", "position": "qa" },
"actions": [
{ "id": "<actionId>", "type": "save", "time": "2026-09-30T16:02:44.837Z", "ip": "<ip>", "userAgent": "curl/8.16.0", "user": "<userId>" },
{ "id": "<submitActionId>", "type": "submit", "time": "2026-09-30T16:02:47.548Z", "ip": "<ip>", "userAgent": "curl/8.16.0", "user": "<userId>" }
],
"sentEmails": [],
"archivedRenders": [
{
"id": "<renderId>",
"action": "<submitActionId>",
"type": "template",
"template": { "type": "auto" },
"file": {
"name": "Job application.pdf",
"size": 23830,
"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-30T16:02:47.548Z",
"createTime": "2026-09-30T16:02:44.843Z",
"modifyTime": "2026-09-30T16:02:47.562Z",
"changedAt": "2026-09-30T16:02:48.273Z"
},
"uiActions": [{ "type": "message", "text": "The form was submitted successfully." }],
"entryId": "<entryId>"
}The form's after-submit rules ran before the response: this form archives a PDF of every
submission, so archivedRenders already holds it. The emails the form sent are in sentEmails.
An email rule can also notify your own system of each submission: see
Get notified when a form is submitted. The resume links are gone, because
a submitted entry can't be resumed.
When the data breaks a rule of the form, nothing is saved, and the response lists each problem:
{
"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" }
]
}Despite the wording of message, this almost always means the data broke a rule of the form.
Fix the fields named in messages.
Edit an entry
POST /entry/{entryId}/edit changes an entry in
any status except deleted, including a submitted one. It works only with an API key or a login
token, never with a fill link. Its body takes:
data: the new data, applied according toupdateMode(see Update modes).status: optional. Leave it out to keep the entry's status. The allowed changes aresavedtosubmitted,submittedtosaved, andcreatedtosavedorsubmitted. A template can't change status.
To let a person revise a submitted entry, edit it back to saved with {"status": "saved"}. The
response then carries a fresh resumeUrlBewit to send them.
Editing an entry to submitted doesn't run the after-submit rules
An edit runs only the form's after-edit rules. Editing an entry to submitted sends none of the
emails and archives none of the PDFs the form defines for a submission, and it doesn't set
submitTime. To finish an entry the way a person would, with its emails and PDF, submit it with
POST /entry/{entryId}/submit. That works for a
created or saved entry.
This edit moves a saved entry to submitted. The form above archives a PDF on every submission,
yet the edited entry has none:
curl -X POST https://api.tepez.co.il/v1.0/entry/<entryId>/edit \
-H "x-api-key: $TEPEZ_API_KEY" \
-H "Content-Type: application/json" \
-d '{"status": "submitted"}'{
"entry": {
"id": "<entryId>",
"status": "submitted",
"submitIdx": 5,
"archivedRenders": [],
"sentEmails": [],
"actions": [
{ "id": "<actionId>", "type": "save", "time": "2026-09-30T16:02:59.566Z", "ip": "<ip>", "userAgent": "curl/8.16.0", "user": "<userId>" },
{ "id": "<actionId2>", "type": "edit", "time": "2026-09-30T16:03:00.499Z", "ip": "<ip>", "userAgent": "curl/8.16.0", "user": "<userId>" }
]
},
"uiActions": [{ "type": "message", "text": "The entry was updated successfully." }]
}Update modes
updateMode in the body of an edit decides how the data you send combines with the entry's
current data:
replace, the default: yourdatabecomes the entry's whole data. Every key you leave out is removed.assign: each top-level key you send replaces that key. Other top-level keys stay.merge: yourdatamerges into the current data, key by key, at every depth. Arrays merge item by item, by position.
Each example below starts from this entry data:
{
"fullName": "Dana Levi",
"address": { "city": "Haifa", "street": "Herzl 10" },
"references": [
{ "name": "Yossi Cohen", "phone": "050-0000000" },
{ "name": "Rina Katz", "phone": "052-0000000" }
]
}and sends the same data with a different updateMode:
curl -X POST https://api.tepez.co.il/v1.0/entry/<entryId>/edit \
-H "x-api-key: $TEPEZ_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"data": {
"address": {"city": "Tel Aviv"},
"references": [{"name": "Avi Mor"}]
},
"updateMode": "merge"
}'The entry's data after each mode:
{
"address": { "city": "Tel Aviv" },
"references": [{ "name": "Avi Mor" }]
}fullName and address.street are gone, and references has one row.
merge can't make an array shorter
merge combines arrays by position, so sending a shorter array overwrites the first items and
keeps the rest. The request succeeds, and the old rows stay. To remove rows, send the whole
array, and every other key of the same top-level property, with assign. Use merge to change
single values deep in the data, and assign to replace an array or an object whole.
Update and delete entries shows a complete correction of a submitted entry, and how to delete entries.
Template entries and clone links
A template entry (status template) is a prefilled starting point. Each person who opens its
clone link starts a new entry with a copy of the template's data. The template itself doesn't
change. Use it for one link that many people fill, such as a form with your company's details
already filled in.
POST /entry/template creates one. With
createShortUrl=true, the default, it also creates a short link:
curl -X POST "https://api.tepez.co.il/v1.0/entry/template?createShortUrl=true" \
-H "x-api-key: $TEPEZ_API_KEY" \
-H "Content-Type: application/json" \
-d '{"form": "<formId>", "data": {"position": "qa", "address": {"city": "Haifa"}}}'{
"formId": "<formId>",
"entryId": "<templateEntryId>",
"shortUrl": "https://tpz.link/<code>"
}shortUrlredirects to the template'scloneUrl,https://app.tepez.co.il/c/<templateEntryId>/. Anyone can open either link without signing in, so share them freely.- A template is checked against the form's rules only if the form is set to check templates
(
formSchema.validateTemplate). - To change a template's data, use
POST /entry/{entryId}/edit, usually withupdateMode: "merge". The entry stays a template. The change applies only to entries started after it. PATCH /entry/{entryId}sets a template'sname, which the management app shows. It changes nothing else.
Pre-issue empty entries
POST /form/{formId}/generateEntries
creates num empty entries at once, up to 10,000 per request. Each comes back with its own
resumeUrlBewit, so you can hand out personal fill links before anyone starts:
curl -X POST https://api.tepez.co.il/v1.0/form/<formId>/generateEntries \
-H "x-api-key: $TEPEZ_API_KEY" \
-H "Content-Type: application/json" \
-d '{"num": 2}'{
"message": "2 entries where created",
"createdEntries": [
{
"id": "<entryId>",
"form": "<formId>",
"status": "created",
"idx": 11,
"actions": [
{ "id": "<actionId>", "type": "create", "time": "2026-09-30T16:03:24.465Z", "ip": "<ip>", "userAgent": "curl/8.16.0", "user": "<userId>" }
],
"bewit": "<bewit>",
"resumeUrlBewit": "https://app.tepez.co.il/e/<entryId>/?b=<bewit>",
"resumeUrl": "https://app.tepez.co.il/e/<entryId>/",
"createTime": "2026-09-30T16:03:24.465Z"
},
{
"id": "<entryId2>",
"status": "created",
"idx": 12,
"resumeUrlBewit": "https://app.tepez.co.il/e/<entryId2>/?b=<bewit2>"
}
]
}A created entry has no data. To prefill it before you send the link, save it with
POST /entry/{entryId}/save.
An entry's history
actions lists everything that happened to an entry, oldest first. Each action has a type, the
time, the ip and userAgent of the request, and the user when the request was
authenticated. The types are create, save, submit, edit, template, clone, resume,
render and delete.
Other parts of the entry point back into this list: each item of archivedRenders and
sentEmails has an action, the id of the action that produced it. In the submit response
above, the archived PDF's action is the id of the submit action.
actions is the only per-entry history. The account's activity log,
GET /log, records sign-ins and password changes, not entry
activity.
Troubleshooting
| Response | Cause and fix |
|---|---|
400 entry_failed_validation | The data broke a rule of the form. Each item of messages names the field in modelPath. See Form data schema for the value each field takes. |
400 "query.status" must be one of [object, 0, 1, 2, 3, 4, 5] | query takes the status number, not its label: "status":2, not "status":"submitted". |
403 You cannot save this form entry because it was already submitted. | Save and submit work only on created and saved entries. Edit the entry instead, or edit it back to saved first. |
403 Can't change the status of the entry to this status. | The edit asked for a status change that isn't allowed, such as a template to submitted. |
200, but the entry has no archived PDF and no emails were sent | The entry was edited to submitted. Submit it instead: an edit runs only the after-edit rules. |
200, but an array still has its old rows | merge can't shorten an array. Send the whole top-level property with assign. |
200, but keys you didn't send are gone | The edit had no updateMode, so it used replace. Send updateMode: "merge" or "assign". |
403 This form entry was already deleted and cannot be edited. | The entry was deleted, and a deleted entry can't change. See Delete an entry. |
Errors explains the error format and lists more messages.
Every response carries an x-tepez-request-id header. Quote it when you contact us, so we can find
your request in our logs.
Question about this page? Email support@tepez.co.il