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:

FieldTypeRequiredDescription
namestringYesDisplay name for the form
publicKeyobjectYesRSA public key as a JWK (2048–4096 bits, e = AQAB). Requests containing private key fields are rejected
settings.encryptionbooleanNoEnable encryption (default: true)
settings.piiStripbooleanNoStrip PII (default: false)
settings.webhookUrlstringNoURL to receive webhook notifications
settings.allowedOriginsarrayNoCORS 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:

ParameterTypeRequiredDescription
confirmstringYesMust 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:

ParameterTypeRequiredDescription
periodstringNoday, 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