Business
REST API
Pull submissions into your CRM, ticket system or your own scripts automatically – with a plain JSON API.
Authentication
Every request needs an API key in the Authorization header. As the owner of a Business account you create keys under "API" in your account.
curl https://www.formpost.eu/api/v1/submissions?since=2026-10-01T00:00:00Z \
-H "Authorization: Bearer fp_..."
A key belongs to the account, not to a person, and sees all forms of the account. If the Business plan expires, the API responds with 403 until it is booked again.
Endpoints
GET /forms
All forms of the account.
GET /forms/{id}
One form. The ID is the identifier from the form address (/f/abc123).
GET /submissions
Submissions of all forms, newest first.
GET /forms/{id}/submissions
Submissions of one form.
GET /submissions/{id}
A single submission.
PATCH /submissions/{id}
Mark as spam or release.
DELETE /submissions/{id}
Delete a submission permanently.
All paths are relative to https://www.formpost.eu/api/v1. Responses are JSON.
List parameters
filter- valid (default), spam or all – the same filters as in the inbox.
since- Only submissions from this point in time (ISO 8601). Use it to regularly fetch only what is new.
page- Page, starting at 1.
per_page- Entries per page, 1 to 100 (default 50).
Responses
Lists are in data, paging details in meta.
{
"data": [
{
"id": 4711,
"form_id": "abc123",
"state": "valid",
"spam": false,
"verified": true,
"encrypted": false,
"data": { "email": "anna@example.com", "message": "Hallo!" },
"encrypted_data": null,
"recipient_email": "info@example.com",
"referer": "https://example.com/kontakt",
"created_at": "2026-10-12T09:30:00Z"
}
],
"meta": { "page": 1, "per_page": 50, "total": 1, "total_pages": 1, "filter": "valid" }
}
For encrypted forms, data is empty and encrypted_data contains the sealed envelope. We do not know the content through the API either – it can only be opened with your private key. More under Zero-knowledge encryption.
Mark spam
spam: true moves a submission to spam, spam: false releases it again.
curl -X PATCH https://www.formpost.eu/api/v1/submissions/4711 \
-H "Authorization: Bearer fp_..." \
-H "Content-Type: application/json" \
-d '{"spam": true}'
Errors
Errors come with a matching HTTP status and a machine-readable code.
{ "error": { "code": "unauthorized", "message": "Missing or invalid API key" } }
401– Key missing, wrong or revoked.403– The account has no active Business plan.404– Form or submission does not exist in this account.400– Invalid parameter, e.g. a wrong date format.429– Too many requests – at most 120 per minute and key.
Webhooks
Instead of polling, you can have every new submission sent to you. We then POST JSON to your URL – as soon as the submission has passed spam protection, at the same time as the email.
POST https://example.com/formpost-webhook
Content-Type: application/json
FormPost-Event: submission.created
FormPost-Delivery: 5f0c2c1e-8a8e-4c4a-9b7e-2d6b1d0f7a11
FormPost-Signature: t=1760261400,v1=3b1f…
{
"id": "5f0c2c1e-8a8e-4c4a-9b7e-2d6b1d0f7a11",
"type": "submission.created",
"created_at": "2026-10-12T09:30:00Z",
"data": {
"submission": { "id": 4711, "form_id": "abc123", "state": "valid", "data": { "email": "anna@example.com" }, … },
"form": { "id": "abc123", "name": "Kontakt", … }
}
}
- Events: submission.created for new, valid submissions and ping for the test message from your account.
- If your server does not answer with a 2xx status within 10 seconds, we retry up to six times over about nine hours. After ten consecutive failed deliveries we disable the webhook.
- For encrypted forms, the webhook only contains the sealed envelope, just like the API.
- Every delivery has a unique ID (FormPost-Delivery). If the same ID arrives twice, process it only once.
Verify the signature
Every message is signed with the webhook secret (HMAC-SHA256 over timestamp, dot and body). Check the signature before processing anything – that way your server knows the message really comes from us.
<?php
$secret = 'whsec_...';
$body = file_get_contents('php://input');
$header = $_SERVER['HTTP_FORMPOST_SIGNATURE'] ?? '';
parse_str(str_replace(',', '&', $header), $sig); // ['t' => ..., 'v1' => ...]
$expected = hash_hmac('sha256', $sig['t'] . '.' . $body, $secret);
if (!hash_equals($expected, $sig['v1'] ?? '') || abs(time() - (int) $sig['t']) > 300) {
http_response_code(400);
exit;
}
$event = json_decode($body, true);
// $event['data']['submission'] verarbeiten …
http_response_code(200);
Get started
The REST API is included in the Business plan – together with team access and up to 1,000 forms.
Create an API key