Let your users fill a form in your app
Send a user of your system to a prefilled Tepez form, bring them back when they submit, and read what they submitted and its PDF.
This guide connects a Tepez form to a process in your own system, which we call your app. Your app creates an entry for the user, sends them to fill it in, brings them back when they submit, and reads the data and the PDF. The same flow works whether the user reaches the form by a redirect, by an email, or inside your page.
The flow
Your app Tepez The user
| | |
| 1. POST /entry/save ---------> | |
| <-- entry.id, resumeUrlBewit | |
| | |
| 2. send the user to resumeUrlBewit ------------------------------> |
| | <------------- fills in, submits |
| | runs the after-submit rules: |
| | PDF, emails |
| | |
| <------- 3. redirect to your callback URL, ?entryId=<entryId> ---- |
| | |
| 4. GET /entry/{entryId} -----> | |
| <-- status, data, PDF link | |- Your app creates an entry for the user and stores its id.
- Your app sends the user to the entry's link.
- After the user submits, Tepez redirects them to a page in your app.
- Your app reads the entry from the API: its status, its data and its PDF.
Your app does steps 1 and 4 from its server, with an API key. Step 3 is a rule on the form.
1. Create the entry
Create a draft entry with POST /entry/save, with the
values your app already has. Prefill a form
shows the request and its full response. Store two values from it next to the user in your app:
entry.id: how your app reads the entry later.entry.resumeUrlBewit: the user's personal link to the entry.
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": "Dana Levi", "email": "dana@example.com"}}'{
"entry": {
"id": "<entryId>",
"status": "saved",
"data": { "fullName": "Dana Levi", "email": "dana@example.com" },
"resumeUrlBewit": "https://app.tepez.co.il/e/<entryId>/?b=<bewit>"
},
"entryId": "<entryId>"
}When the user comes back to this step before submitting, for example after closing the tab, send them to the same entry rather than creating another. Their draft is still there.
2. Send the user to the form
Redirect the user's browser to resumeUrlBewit, send it to them by email or text message, or
open it in an iframe on your page (see Embed a form). Use the link exactly as the
API returned it. The link needs no sign-in: it works until the entry is submitted, and for at most
2 years.
3. Bring the user back after they submit
By default, after the user submits, the form shows a thank-you message. To bring them back to your app, the form redirects them to a URL instead. The redirect is a rule on the form, one of its after-submit rules. The Tepez team usually sets it up for you: tell us the URL to redirect to.
The URL can be the same for everyone, or built from the entry:
- Static:
https://your-app.example.com/tepez/done. - With the entry id:
https://your-app.example.com/tepez/done?entryId={{ data.entry.id }}. Tepez replaces{{ data.entry.id }}with the submitted entry's id, so the page knows which entry to check. - Chosen by your app per entry: add a hidden text field to the form, such as
redirectUrl, and set the redirect URL to{{ model.redirectUrl }}. Your app puts the URL in that field when it creates the entry, so each process can return the user to its own page.
GET /form/{formId} shows the rule under postSubmit.ui:
"postSubmit": {
"ui": [
{
"type": "redirect",
"redirectOptions": { "url": "https://your-app.example.com/tepez/done?entryId={{ data.entry.id }}" }
}
]
}A user who submits entry <entryId> lands on
https://your-app.example.com/tepez/done?entryId=<entryId>.
- The redirect happens after Tepez has saved the entry, archived its PDF and sent its emails. By the time the user reaches your page, everything in step 4 is ready.
- The URL must start with
https://orhttp://once the values are filled in. If it doesn't, for example becauseredirectUrlis empty, Tepez skips the redirect and shows the thank-you message instead. - Don't trust the callback on its own. Anyone can open your callback URL with any
entryId, and a user who closes the tab right after submitting never reaches it. Treat the callback as a signal to check the entry, and check it again whenever the user returns to your app.
4. Read the entry
Read the entry with GET /entry/{entryId}, using the id
your app stored in step 1, not an id taken from the callback URL alone.
curl https://api.tepez.co.il/v1.0/entry/<entryId> \
-H "x-api-key: $TEPEZ_API_KEY"A submitted entry, trimmed:
{
"id": "<entryId>",
"form": "<formId>",
"status": "submitted",
"idx": 1,
"submitIdx": 1,
"data": {
"fullName": "Dana Levi",
"email": "dana@example.com",
"address": { "city": "Haifa", "street": "Herzl 10" },
"phone": "050-1234567"
},
"actions": [
{ "id": "<actionId>", "type": "save", "time": "2026-09-30T16:43:48.664Z", "ip": "<ip>", "userAgent": "curl/8.16.0", "user": "<userId>" },
{ "id": "<actionId2>", "type": "resume", "time": "2026-09-30T16:47:11.372Z", "ip": "<ip>", "userAgent": "Mozilla/5.0 …" },
{ "id": "<submitActionId>", "type": "submit", "time": "2026-09-30T16:47:23.514Z", "ip": "<ip>", "userAgent": "Mozilla/5.0 …" }
],
"archivedRenders": [
{
"id": "<renderId>",
"action": "<submitActionId>",
"type": "template",
"template": { "type": "auto" },
"file": {
"name": "Job application.pdf",
"size": 18068,
"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:47:23.514Z",
"createTime": "2026-09-30T16:43:48.668Z",
"modifyTime": "2026-09-30T16:47:23.521Z",
"changedAt": "2026-09-30T16:47:24.193Z"
}Act on the entry's status:
status | What it means | What your app does |
|---|---|---|
submitted | The user finished. | Store data, and download the PDF from archivedRenders[].file.url if the form archives one. Files and PDFs explains the PDF. The process is done. |
saved | The user hasn't submitted yet: they saved a draft, closed the tab, or haven't started. | Send the user back to resumeUrlBewit, which the response includes while the entry is saved. |
The submitted entry has no resumeUrlBewit: the link stopped working when the user submitted.
actions shows the entry's history, including each time the user opened the link (resume).
Let the user revise a submitted entry
To let the user change an entry they already submitted, edit it back to saved with
POST /entry/{entryId}/edit and
{"status": "saved"}. The response carries a new resumeUrlBewit: send the user there, and
repeat steps 3 and 4. See Edit an entry.
To correct a submitted entry yourself, or to delete an entry when the process ends, see Update and delete entries. To submit an entry on the user's behalf, without sending them to the form, see Submit entries from your server.
Troubleshooting
| Symptom | Cause and fix |
|---|---|
| The user sees the thank-you message instead of returning to your app | The form has no redirect rule, or its URL is empty or isn't an http(s) URL once the values are filled in. Check postSubmit.ui in GET /form/{formId}. |
The callback URL arrives with an empty entryId | The expression in the rule is misspelled, so it evaluates to nothing. Ask us to check the form's redirect rule. |
The entry is saved when the user reaches your callback | The user reached the callback without submitting, for example by going back in the browser. Send them to resumeUrlBewit again. |
The link shows You cannot resume editing this form entry because it was already submitted. | The entry was submitted. Read it with GET /entry/{entryId}, or edit it back to saved to let the user revise it. |
No archivedRenders on the submitted entry | The form doesn't archive a PDF on submit. See Files and PDFs to render one, or ask us to set the form to archive one. |
403 You have insufficient permissions to perform the action. | The entry belongs to another account. A key reaches only its own account. |
404 Could not find an entry with ID … | The id is wrong. Check that you stored entry.id, not the form id. |
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