Forms API
Create, update, list, and delete forms programmatically.
List Forms
Example Request:
curl https://veilforms.com/api/forms \
-H "Authorization: Bearer vf_live_xxx"
Response:
{
"forms": [
{
"id": "vf-abc123",
"name": "Contact Form",
"createdAt": 1699920000000,
"submissionCount": 142,
"publicKey": {
"kty": "RSA",
"n": "0vx7agoebG...",
"e": "AQAB"
},
"settings": {
"encryption": true,
"piiStrip": true
}
},
{
"id": "vf-def456",
"name": "Feedback Form",
"createdAt": 1699920100000,
"submissionCount": 58,
"publicKey": {...},
"settings": {...}
}
],
"total": 2
}
Get Form
Example Request:
curl https://veilforms.com/api/forms/vf-abc123 \
-H "Authorization: Bearer vf_live_xxx"
Response:
{
"form": {
"id": "vf-abc123",
"name": "Contact Form",
"createdAt": 1699920000000,
"updatedAt": 1699930000000,
"submissionCount": 142,
"publicKey": {
"kty": "RSA",
"n": "0vx7agoebG...",
"e": "AQAB"
},
"settings": {
"encryption": true,
"piiStrip": true,
"webhookUrl": "https://yoursite.com/webhook",
"allowedOrigins": ["https://yoursite.com"]
},
"embedCode": "<script src=\"https://veilforms.com/js/veilforms.min.js\"></script>\n<script>VeilForms.init('vf-abc123', {...});</script>"
}
}
Create Form
Generate the key pair (browser or Node 18+; the same parameters the SDK encrypts with):
const pair = await crypto.subtle.generateKey(
{ name: 'RSA-OAEP', modulusLength: 2048, publicExponent: new Uint8Array([1, 0, 1]), hash: 'SHA-256' },
true,
['encrypt', 'decrypt']
);
const publicKey = await crypto.subtle.exportKey('jwk', pair.publicKey); // send this
const privateKey = await crypto.subtle.exportKey('jwk', pair.privateKey); // keep this
Request Body:
{
"name": "Contact Form",
"publicKey": {
"kty": "RSA",
"n": "0vx7agoebG...",
"e": "AQAB"
},
"settings": {
"encryption": true,
"piiStrip": true,
"webhookUrl": "https://yoursite.com/webhook",
"allowedOrigins": ["https://yoursite.com"]
}
}
Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Display name for the form |
publicKey | object | Yes | RSA public key as a JWK (2048–4096 bits, e = AQAB). Requests containing private key fields are rejected |
settings.encryption | boolean | No | Enable encryption (default: true) |
settings.piiStrip | boolean | No | Strip PII (default: false) |
settings.webhookUrl | string | No | URL to receive webhook notifications |
settings.allowedOrigins | array | No | CORS origins (default: ["*"]) |
Example Request:
curl -X POST https://veilforms.com/api/forms \
-H "Authorization: Bearer vf_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"name": "Contact Form",
"publicKey": {"kty": "RSA", "n": "0vx7agoebG...", "e": "AQAB"},
"settings": {
"encryption": true,
"piiStrip": true
}
}'
Response:
{
"form": {
"id": "vf-abc123",
"name": "Contact Form",
"createdAt": 1699920000000,
"publicKey": {
"kty": "RSA",
"n": "0vx7agoebG...",
"e": "AQAB",
"alg": "RSA-OAEP-256"
},
"settings": {
"encryption": true,
"piiStrip": true
}
}
}
Update Form
Request Body:
{
"name": "Updated Contact Form",
"settings": {
"piiStrip": true,
"webhookUrl": "https://newsite.com/webhook"
}
}
Example Request:
curl -X PATCH https://veilforms.com/api/forms/vf-abc123 \
-H "Authorization: Bearer vf_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"name": "Updated Contact Form",
"settings": {
"webhookUrl": "https://newsite.com/webhook"
}
}'
Response:
{
"form": {
"id": "vf-abc123",
"name": "Updated Contact Form",
"updatedAt": 1699930000000,
"settings": {
"encryption": true,
"piiStrip": true,
"webhookUrl": "https://newsite.com/webhook"
}
}
}
Delete Form
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
confirm | string | Yes | Must be DELETE_FORM |
Example Request:
curl -X DELETE "https://veilforms.com/api/forms/vf-abc123?confirm=DELETE_FORM" \
-H "Authorization: Bearer vf_live_xxx"
Response:
{
"success": true,
"deleted": {
"formId": "vf-abc123",
"submissionsDeleted": 142
}
}
Rotate Keys
Example Request:
curl -X POST https://veilforms.com/api/forms/vf-abc123/regenerate-keys \
-H "Authorization: Bearer vf_live_xxx" \
-H "Content-Type: application/json" \
-d '{"publicKey": {"kty": "RSA", "n": "NEW_KEY...", "e": "AQAB"}}'
Response:
{
"form": {
"id": "vf-abc123",
"publicKey": {
"kty": "RSA",
"n": "NEW_KEY...",
"e": "AQAB",
"alg": "RSA-OAEP-256"
},
"keyRotatedAt": "2026-11-01T12:00:00.000Z"
},
"warning": "New submissions use the new key. Keep your old private key: earlier submissions still need it."
}
Form Statistics
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
period | string | No | day, week, month, year (default: month) |
Example Request:
curl "https://veilforms.com/api/forms/vf-abc123/stats?period=week" \
-H "Authorization: Bearer vf_live_xxx"
Response:
{
"formId": "vf-abc123",
"period": "week",
"stats": {
"totalSubmissions": 142,
"periodSubmissions": 23,
"avgPerDay": 3.3,
"timeline": [
{ "date": "2024-01-01", "count": 5 },
{ "date": "2024-01-02", "count": 3 },
{ "date": "2024-01-03", "count": 4 },
{ "date": "2024-01-04", "count": 2 },
{ "date": "2024-01-05", "count": 6 },
{ "date": "2024-01-06", "count": 1 },
{ "date": "2024-01-07", "count": 2 }
]
}
}
Error Responses
404 Not Found
{
"error": "not_found",
"message": "Form not found"
}
400 Bad Request
{
"error": "bad_request",
"message": "Invalid form settings",
"details": {
"webhookUrl": "Must be a valid HTTPS URL"
}
}
409 Conflict
{
"error": "conflict",
"message": "A form with this name already exists"
}
SDK Integration
After creating a form via API, integrate with the SDK:
// privateKey from the key pair you generated before POST /api/forms
const { form } = response;
// Store the private key securely (your server/secrets manager)
saveToSecretsManager(form.id, privateKey);
// Generate embed code for client
const embedCode = `
<script src="https://veilforms.com/js/veilforms.min.js"></script>
<script>
VeilForms.init('${form.id}', {
publicKey: ${JSON.stringify(form.publicKey)},
encryption: true
});
</script>
`;
Next Steps
- Submissions API — Manage submissions
- Webhooks — Real-time notifications
- Authentication — API keys