Skip to content

Intake Forms API

Intake forms are public or internal web forms that create issues when submitted. Standalone products use them for customer-facing submissions (GDPR requests, support tickets, contract requests).

Public Submission (No Auth Required)

Submit to a form using its slug. No authentication needed for public forms.

Embed endpoint (JSON)

POST https://kiket.dev/intake/{slug}/embed
Content-Type: application/json

{
  "intake_submission": {
    "data": {
      "title": "GDPR Data Access Request",
      "request_type": "access",
      "requester_email": "jan@example.com",
      "description": "I would like a copy of all personal data you hold about me."
    }
  }
}

Response:

{
  "success": true,
  "message": "Thank you for your submission!",
  "submission_id": "a1b2c3d4-...",
  "redirect_url": "https://spravce-gdpr.cz/thank-you"
}

Error response (422):

{
  "success": false,
  "errors": ["Title is required", "Request type must be selected"]
}

Organization-scoped submission

POST https://kiket.dev/intake/{org_slug}/{form_slug}/embed

Prefill fields via URL

GET https://kiket.dev/intake/gdpr-request?prefill_email=jan@example.com&prefill_request_type=access

Query parameters matching prefill_{field_key} pre-populate form fields.

Rate limiting

Public forms enforce per-IP rate limits (configurable per form, default from plan). Returns 429 Too Many Requests when exceeded.

CAPTCHA

Public forms can require Cloudflare Turnstile verification. Pass the token as cf-turnstile-response in the submission.

CORS for embeds

Forms support cross-origin embedding. Configure allowed_origins on the intake form (supports wildcards: https://*.example.com). The embed endpoint sets appropriate CORS headers and X-Frame-Options: ALLOWALL.


Authenticated API

Requires Authorization: Bearer <token>.

List forms

GET /api/v1/projects/{project_id}/intake_forms

Response:

{
  "intake_forms": [
    {
      "id": "uuid",
      "key": "gdpr-request",
      "slug": "gdpr-request",
      "name": "GDPR Data Subject Request",
      "description": "Public form for GDPR data subject requests",
      "active": true,
      "public": true,
      "requires_approval": true,
      "default_issue_type": "dsar",
      "default_priority": "high",
      "form_url": "https://kiket.dev/intake/gdpr-request",
      "created_at": "2026-04-01T00:00:00Z"
    }
  ]
}

Show form with fields

GET /api/v1/projects/{project_id}/intake_forms/{id}

Returns the form configuration including all field definitions (types, labels, options, validation rules, mapping).

List submissions

GET /api/v1/projects/{project_id}/intake_forms/{form_id}/submissions

# Filters
?status=pending          # pending, approved, rejected, converted
?since=2026-01-01T00:00:00Z
?until=2026-12-31T23:59:59Z
?page=1&per_page=25      # max 100

Response:

{
  "submissions": [
    {
      "id": "uuid",
      "status": "pending",
      "submitter_name": "Jan Novak",
      "submitter_email": "jan@example.com",
      "submitted_at": "2026-04-01T12:00:00Z",
      "issue_id": null,
      "notes": null
    }
  ],
  "meta": {
    "current_page": 1,
    "total_pages": 5,
    "total_count": 123
  }
}

Show submission

GET /api/v1/projects/{project_id}/intake_forms/{form_id}/submissions/{id}

Response includes full data (field values), attachments (with download URLs), and linked issue if converted.

{
  "submission": {
    "id": "uuid",
    "status": "converted",
    "data": {
      "title": "GDPR Data Access Request",
      "request_type": "access",
      "requester_email": "jan@example.com",
      "description": "..."
    },
    "attachments": [
      {
        "id": "uuid",
        "filename": "id-verification.pdf",
        "content_type": "application/pdf",
        "byte_size": 45000,
        "url": "https://..."
      }
    ],
    "issue": {
      "id": 456,
      "key": "GDPR-42",
      "title": "GDPR Data Access Request",
      "status": "open"
    },
    "submitted_at": "2026-04-01T12:00:00Z",
    "processed_at": "2026-04-01T14:30:00Z"
  }
}

Create submission via API

POST /api/v1/projects/{project_id}/intake_forms/{form_id}/submissions

{
  "submission": {
    "data": {
      "title": "Feature request",
      "description": "Add dark mode support"
    }
  }
}

No authentication required for public forms.


Processing Pipeline

  1. Submit — Form data validated against field definitions
  2. Rate limit — Per-IP, per-form, per-hour check
  3. CAPTCHA — Turnstile verification (if enabled)
  4. SaveIntakeSubmission created with status: pending
  5. Process — If requires_approval: false, automatically converts to issue:
  6. Maps fields to issue attributes (title, description, priority, assignee, custom_fields)
  7. Creates issue in the project with configured workflow
  8. Updates submission status: converted with issue_id
  9. If requires_approval: true — Stays as pending until admin approves via UI

Submission Statuses

Status Description
pending Awaiting review (when requires_approval: true)
approved Approved by admin, converted to issue
rejected Rejected by admin (with optional notes)
converted Automatically converted to issue