Ulifyi Forms
Developer API
Integrate form submissions directly into your own systems. Pull data via REST, or receive real-time pushes with signed webhooks.
Overview
The Ulifyi Forms Developer API lets you read form definitions and submissions programmatically, and update submission statuses from your own backend. All authenticated requests use API keys scoped to a single organization.
You need an active Ulifyi Business organization. Generate your API key in the Developer tab of the business dashboard.
Base URL
https://api.uli.fyiAll developer API endpoints are prefixed with /api/v1/developer/organizations/{oId}. Replace {oId} with your organization ID (visible in the Dashboard URL).
Authentication
Pass your API key in the Authorization header as a Bearer token, or alternatively in the X-API-Key header.
# Bearer token (recommended)
curl https://api.uli.fyi/api/v1/developer/organizations/{oId}/forms \
-H "Authorization: Bearer uf_live_your_api_key_here"
# Alternatively
curl https://api.uli.fyi/api/v1/developer/organizations/{oId}/forms \
-H "X-API-Key: uf_live_your_api_key_here"Never expose your API key in frontend code or public repositories. Keys are shown only once at creation and cannot be retrieved afterwards. Revoke compromised keys immediately from the Dashboard.
Error Codes
All error responses follow this shape:
{
"status": 401,
"error": "invalid_api_key"
}| HTTP Status | error | Meaning |
|---|---|---|
401 | missing_api_key | No key provided |
401 | invalid_api_key | Key not found or revoked |
400 | missing_organization_id | oId missing in path |
404 | form_not_found | formId doesn't belong to this org |
404 | submission_not_found | submissionId not found |
429 | rate_limit_exceeded | 120 req/min per key |
500 | internal_server_error | Server error |
Rate Limits
Developer API endpoints allow 120 requests per minute per organization. When exceeded, the server returns 429 Too Many Requests. The standard Retry-After header is included.
Forms API
List Forms
/api/v1/developer/organizations/{oId}/formsReturns all forms belonging to the organization.
curl https://api.uli.fyi/api/v1/developer/organizations/ORG_ID/forms \
-H "Authorization: Bearer uf_live_your_api_key"Response
{
"status": 200,
"forms": [
{
"formId": "1748000000_abc123",
"oId": "your_org_id",
"name": "Schüleranmeldung 2025",
"description": null,
"template": "driving_school",
"status": "active",
"formMode": "wizard",
"fields": [ ... ],
"branding": { "primaryColor": "#000000", "logoUrl": null },
"customDomain": null,
"submissionCount": 42,
"newSubmissionCount": 3,
"createdAt": "2025-05-01T10:00:00.000Z",
"updatedAt": "2025-05-10T14:30:00.000Z"
}
]
}Submissions API
List Submissions
/api/v1/developer/organizations/{oId}/forms/{formId}/submissionsReturns paginated submissions for a form. Sorted by submittedAt descending.
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
status | string | — | new | read | done | archived |
limit | number | 50 | Max 100 |
offset | number | 0 | Pagination offset |
curl "https://api.uli.fyi/api/v1/developer/organizations/ORG_ID/forms/FORM_ID/submissions?status=new&limit=20" \
-H "Authorization: Bearer uf_live_your_api_key"Response
{
"status": 200,
"submissions": [
{
"submissionId": "1748000001_xyz789",
"formId": "1748000000_abc123",
"oId": "your_org_id",
"displayName": "Max Mustermann",
"email": "[email protected]",
"ulifyiUserId": null,
"status": "new",
"data": {
"field_name": "Max Mustermann",
"field_email": "[email protected]",
"field_phone": "+49 151 12345678"
},
"submittedAt": "2025-05-28T09:15:00.000Z",
"updatedAt": "2025-05-28T09:15:00.000Z"
}
],
"total": 42,
"limit": 20,
"offset": 0
}Update Submission Status
/api/v1/developer/organizations/{oId}/forms/{formId}/submissions/{submissionId}Updates the processing status of a submission.
Request Body
{
"status": "done" // "new" | "read" | "done" | "archived"
}curl -X PATCH \
"https://api.uli.fyi/api/v1/developer/organizations/ORG_ID/forms/FORM_ID/submissions/SUBMISSION_ID" \
-H "Authorization: Bearer uf_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{"status": "done"}'Response
{
"status": 200,
"submission": {
"submissionId": "1748000001_xyz789",
"formId": "1748000000_abc123",
"oId": "your_org_id",
"status": "done",
"updatedAt": "2025-05-28T10:00:00.000Z"
}
}Webhooks
Overview
Webhooks deliver real-time notifications to your server when events occur in Ulifyi Forms. When a new form submission arrives, Ulifyi sends an HTTP POST request to your configured endpoint within milliseconds — no polling required.
Configure webhooks in the Developer tab of the Business Dashboard. Each webhook has a unique signing secret that you use to verify that requests genuinely originate from Ulifyi.
Events
| Event | Trigger |
|---|---|
form.submission.created | A visitor submits a public form |
Signature Verification
Every webhook request includes an X-Ulifyi-Signature header. Verify it before processing the payload:
X-Ulifyi-Signature: sha256=3d5f7a...
X-Ulifyi-Event: form.submission.created
X-Ulifyi-Delivery: webhook_id_1748000000
User-Agent: Ulifyi-Webhooks/1.0Verification — Node.js
const crypto = require("crypto");
function verifySignature(rawBody, signature, secret) {
const expected = "sha256=" + crypto
.createHmac("sha256", secret)
.update(rawBody)
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}
// Express example
app.post("/webhook", express.raw({ type: "application/json" }), (req, res) => {
const sig = req.headers["x-ulifyi-signature"];
if (!verifySignature(req.body, sig, process.env.WEBHOOK_SECRET)) {
return res.status(401).send("Invalid signature");
}
const event = JSON.parse(req.body);
// handle event...
res.sendStatus(200);
});Verification — Python
import hmac
import hashlib
def verify_signature(raw_body: bytes, signature: str, secret: str) -> bool:
expected = "sha256=" + hmac.new(
secret.encode(), raw_body, hashlib.sha256
).hexdigest()
return hmac.compare_digest(signature, expected)
# Flask example
@app.route("/webhook", methods=["POST"])
def webhook():
sig = request.headers.get("X-Ulifyi-Signature", "")
if not verify_signature(request.get_data(), sig, WEBHOOK_SECRET):
return "Invalid signature", 401
event = request.get_json()
# handle event...
return "", 200Ulifyi expects a 2xx response within 8 seconds. Failed deliveries are not automatically retried — use the Test button in the Dashboard to manually re-test your endpoint.
Payload Reference
form.submission.created
{
"event": "form.submission.created",
"timestamp": "2025-05-28T09:15:00.000Z",
"data": {
"submissionId": "1748000001_xyz789",
"formId": "1748000000_abc123",
"oId": "your_org_id",
"displayName": "Max Mustermann",
"email": "[email protected]",
"ulifyiUserId": null,
"status": "new",
"data": {
"field_id_1": "field value",
"field_id_2": "another value"
},
"submittedAt": "2025-05-28T09:15:00.000Z"
}
}| Field | Type | Description |
|---|---|---|
event | string | Event type |
timestamp | ISO 8601 | When the event fired |
data.submissionId | string | Unique submission ID |
data.formId | string | Form that received the submission |
data.oId | string | Your organization ID |
data.displayName | string|null | Extracted name field (if any) |
data.email | string|null | Extracted email field (if any) |
data.ulifyiUserId | string|null | Ulifyi user ID if submitted while logged in |
data.status | string | Always new on creation |
data.data | object | Key-value map of field responses (keyed by fieldId) |
data.submittedAt | ISO 8601 | Submission timestamp |
Quick Start
Create an API Key
Go to Business → Developer and create a new API key. Copy and store it securely — it's only shown once.
Pull your first submissions
curl "https://api.uli.fyi/api/v1/developer/organizations/YOUR_ORG_ID/forms/YOUR_FORM_ID/submissions" \
-H "Authorization: Bearer uf_live_your_key"Receive real-time updates via webhook
Create a webhook endpoint in your server, configure it in the Dashboard, and verify the X-Ulifyi-Signature header on every request.