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.

Prerequisites

You need an active Ulifyi Business organization. Generate your API key in the Developer tab of the business dashboard.

Base URL

https://api.uli.fyi

All 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"
Security

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 StatuserrorMeaning
401missing_api_keyNo key provided
401invalid_api_keyKey not found or revoked
400missing_organization_idoId missing in path
404form_not_foundformId doesn't belong to this org
404submission_not_foundsubmissionId not found
429rate_limit_exceeded120 req/min per key
500internal_server_errorServer 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

GET/api/v1/developer/organizations/{oId}/forms

Returns 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

GET/api/v1/developer/organizations/{oId}/forms/{formId}/submissions

Returns paginated submissions for a form. Sorted by submittedAt descending.

Query Parameters

ParameterTypeDefaultDescription
statusstringnew | read | done | archived
limitnumber50Max 100
offsetnumber0Pagination 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

PATCH/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

EventTrigger
form.submission.createdA 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.0

Verification — 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 "", 200
Retry behavior

Ulifyi 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"
  }
}
FieldTypeDescription
eventstringEvent type
timestampISO 8601When the event fired
data.submissionIdstringUnique submission ID
data.formIdstringForm that received the submission
data.oIdstringYour organization ID
data.displayNamestring|nullExtracted name field (if any)
data.emailstring|nullExtracted email field (if any)
data.ulifyiUserIdstring|nullUlifyi user ID if submitted while logged in
data.statusstringAlways new on creation
data.dataobjectKey-value map of field responses (keyed by fieldId)
data.submittedAtISO 8601Submission timestamp

Quick Start

1

Create an API Key

Go to Business → Developer and create a new API key. Copy and store it securely — it's only shown once.

2

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"
3

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.