API reference.
The public API has two endpoints for your forms. All responses are JSON, and errors follow the problem-details shape (type, title, status, detail).
Base URL
https://contact.kirandhakal.meEndpoints
| Method & path | Purpose | Auth |
|---|---|---|
POST /v1/forms/{publicKey}/submissions | Create a submission | Public key + allowed origin |
POST /v1/forms/{publicKey}/upload | Upload a file, returns an uploadId | Public key + allowed origin |
GET /v1/submissions/{submissionId} | Read a submission back | Bearer response token |
Request headers
| Header | Required | Notes |
|---|---|---|
Content-Type: application/json | Yes (submissions) | Omit for multipart uploads. |
Idempotency-Key | Recommended | A unique value per submission; reuse on retry. |
Origin | Browser requests | Set by the browser; must be allowlisted. |
Create a submission
POST a JSON object whose keys match the form schema. See Embedding for full examples.
// 202 Accepted (first time) / 200 OK (idempotent replay)
{
"status": "accepted",
"message": "Thanks! We received your message.",
"submissionId": "8c2f…-uuid",
"responseUrl": "https://contact.kirandhakal.me/v1/submissions/8c2f…-uuid",
"responseToken": "43-character-access-token"
}On a first-time success Contact returns 202 with a submissionId, a responseUrl, and a responseToken. Replaying the same Idempotency-Key returns 200 without a new ID. Store the token if you want to let the submitter read the submission back; it is shown only once.
Upload a file
POST multipart/form-data with a single file part. The response includes uploadId, a temporary url, filename, mimetype, size, and expiresAt. Send the uploadId as a field value when you submit. See File uploads.
Read a submission
GET /v1/submissions/{submissionId} with Authorization: Bearer <responseToken> returns the submission’s id, status, payload, and createdAt. The request origin must still be allowlisted, and responses are never cached.
Status codes
| Code | Meaning | Typical cause |
|---|---|---|
202 | Accepted | Submission stored and delivery queued. |
200 | OK | Idempotent replay of an existing submission. |
400 | Bad request | Malformed body or invalid idempotency key. |
401 | Unauthorized | Missing or invalid response token. |
403 | Forbidden | Origin not in the allowlist; uploads disabled. |
404 | Not found | Unknown or disabled form; expired submission. |
413 | Payload too large | File exceeds the size limit; storage quota reached. |
422 | Unprocessable | Schema validation failed; bad upload reference. |
429 | Too many requests | Rate limit or workspace allowance reached. |
500 | Server error | Unexpected failure; safe to retry. |
502 | Bad gateway | Object storage unavailable during upload. |
Error body
Validation failures include an errors array with path, message, and keyword for each problem. Some errors also include a machine-readable code such as CONSENT_REQUIRED, INVALID_UPLOAD_REFERENCE, FILE_TOO_LARGE, UNSUPPORTED_FILE_TYPE, or STORAGE_QUOTA_EXCEEDED.