Webhook Response Format
Your webhook endpoint must return JSON in one of the standard formats below. SingleForm parses the response and displays appropriate messages to the user in the mobile app.
Success Response
Return this when the submission was processed successfully:
{
"success": true,
"data": {
"submissionId": "your-internal-id-123",
"message": "Thank you for your submission!"
}
}| Field | Type | Required | Description |
|---|---|---|---|
success | true | Yes | Must be true for success responses. |
data.submissionId | string | Recommended | Your system’s ID for this submission. Used to link SingleForm analytics to your records. |
data.message | string | No | Displayed to the user in the mobile app. |
data.* | any | No | Additional data is stored but not displayed. |
Error Response
Return this for business logic errors that aren’t tied to specific fields:
{
"success": false,
"error": {
"type": "DUPLICATE_SUBMISSION",
"message": "This email is already registered for this event"
}
}| Field | Type | Required | Description |
|---|---|---|---|
success | false | Yes | Must be false for error responses. |
error.type | string | Yes | Error type code (see Standard Error Types below). |
error.message | string | Yes | Human-readable message displayed to the user. |
Validation Error Response
Return this when specific form fields have validation errors. SingleForm highlights the invalid fields in the mobile app:
{
"success": false,
"error": {
"type": "VALIDATION_FAILED",
"message": "Please correct the highlighted fields",
"fields": {
"email": "Invalid email format",
"phone": "Phone number must be 10 digits"
}
}
}| Field | Type | Required | Description |
|---|---|---|---|
error.type | string | Yes | Must be "VALIDATION_FAILED" for field-level errors. |
error.fields | object | Yes | Keys are field names, values are error messages. |
Standard Error Types
Security Errors (returned by the middleware)
These are returned automatically by the Express middleware when signature verification fails. You don’t need to handle these yourself if you’re using the SDK.
| Type | HTTP Status | Description |
|---|---|---|
MISSING_HEADERS | 401 | Required SingleForm headers are missing |
INVALID_TIMESTAMP | 401 | Timestamp is not a valid number |
TIMESTAMP_EXPIRED | 401 | Request is older than the tolerance window |
INVALID_SIGNATURE | 401 | Signature format is invalid |
SIGNATURE_MISMATCH | 401 | Signature doesn’t match the expected value |
Business Logic Errors (returned by your code)
Use these standard types for common scenarios:
| Type | Recommended Status | Description |
|---|---|---|
VALIDATION_FAILED | 400 | Field-level validation errors. Include the fields object. |
DUPLICATE_SUBMISSION | 400 | A duplicate entry was detected (e.g., same email). |
RATE_LIMITED | 429 | Too many submissions from this user/IP. |
Custom Error Types
You can define your own error types for business-specific logic:
// Custom error types
res.singleformError("EVENT_FULL", "This event has reached maximum capacity");
res.singleformError("REGISTRATION_CLOSED", "Registration is no longer open");
res.singleformError("PROCESSING_ERROR", "Unable to process. Please try again.", 500);SingleForm will display the message to the user regardless of the error type.
HTTP Status Codes
| Status | Meaning | When to Use |
|---|---|---|
| 200 | Success | Submission processed successfully |
| 400 | Bad Request | Validation or business logic error |
| 401 | Unauthorized | Signature verification failed (handled by middleware) |
| 429 | Too Many Requests | Rate limit exceeded |
| 500 | Server Error | Unexpected server-side error |
How SingleForm Handles Your Response
| Response | What Happens |
|---|---|
success: true | Shows success message to user. Records submissionId in analytics. |
success: false with VALIDATION_FAILED | Highlights the invalid fields with error messages in the form. |
success: false with other types | Shows the error message to the user. |
| Non-JSON response | Shows a generic error to the user. |
| Timeout (30s) | Shows a timeout error. The submission may have been processed on your end. |
TypeScript Types
If you’re using TypeScript, the canonical types are available in @singleform/shared:
interface WebhookSuccessResponse {
success: true;
data: {
submissionId?: string;
message?: string;
[key: string]: any;
};
}
interface WebhookErrorResponse {
success: false;
error: {
type: string;
message: string;
fields?: { [fieldName: string]: string };
};
}
type WebhookResponse = WebhookSuccessResponse | WebhookErrorResponse;The Express middleware SDK also exports these types — see Exported Types.
Examples Using the Express SDK
import { singleform } from "@singleform/express-webhook";
app.post(
"/webhooks/singleform",
singleform({ secret: process.env.SINGLEFORM_SECRET }),
async (req, res) => {
const { email, firstName } = req.body;
// Validation error
if (!email) {
return res.singleformValidationError({
email: "Email is required",
});
}
// Duplicate check
const existing = await db.findByEmail(email);
if (existing) {
return res.singleformError(
"DUPLICATE_SUBMISSION",
"This email is already registered"
);
}
// Success
const record = await db.create({ email, firstName });
res.singleformSuccess({
submissionId: record.id,
message: "Registration complete!",
});
}
);