RizzForms — Form Backend Skill
Create forms that collect submissions and deliver them via webhook — no server
code required. RizzForms handles storage, spam filtering, and delivery.
Bundled CLI
This skill includes a CLI at scripts/rizzforms. Use it instead of writing raw
curl commands — it handles authentication, pretty-prints JSON, and has commands
for every API operation.
CODEBLOCK0
Run <skill-path>/scripts/rizzforms help for the full command list.
Prerequisites
You need a RizzForms API key with the admin role (prefix frk_).
Check for an API key: Look for RIZZFORMS_API_KEY in the environment or
~/.config/rizzforms/config.
If no API key exists:
- 1. Sign up at https://forms.rizzness.com/signup
- Go to Account Settings > API Keys > Create API Key (select Admin role)
- Set it: INLINECODE5
Important: Two Subdomains
RizzForms uses two subdomains — using the wrong one is a common mistake:
| Subdomain | Purpose |
|---|
| INLINECODE6 | Form submissions only (/f/ and /json/ routes) |
| INLINECODE9 |
API, dashboard, docs (
/api/ routes) |
HTML form action and JSON submission URLs use forms.rizzness.com.
API management calls use www.rizzness.com.
The CLI handles this automatically.
Workflow
Step 1: Create a form
CODEBLOCK1
The response includes endpoint_token, submission_url, json_url,
embed_html, and examples with ready-to-use curl commands.
Save the endpoint_token — you need it for every subsequent step.
Step 2: Configure a webhook (optional)
If the user wants submissions delivered to an external URL:
CODEBLOCK2
Requirements:
- - URL must use HTTPS
- URL must not resolve to a private/reserved IP
- Save the
signing_secret from the response — it's shown only once
The webhook receives a JSON POST on each submission:
CODEBLOCK3
Each webhook includes an X-RizzForms-Signature header (HMAC-SHA256 of the
body using the signing secret). See references/api.md for verification
examples in Ruby, Node.js, and Python.
Step 3: Test the pipeline
CODEBLOCK4
This sends a test submission with ?test=true for synchronous delivery results.
You can also pass custom JSON:
CODEBLOCK5
If the webhook returns non-2xx or times out, the delivery status will be
"failed" with an error message.
Step 4: Generate HTML
Use the embed_html from the form creation response, or build a custom form.
Always include the hidden honeypot field _hp for spam protection.
CODEBLOCK6
RizzForms captures ALL form fields — there is no fixed schema. Add phone,
company, budget, file upload, radio buttons, checkboxes — whatever is needed.
Match the user's CSS framework:
- - Tailwind CSS: utility classes (
class="block w-full rounded-md border...") - Bootstrap 5: Bootstrap classes (
class="form-control", class="mb-3") - Plain CSS: semantic HTML, no framework classes
Step 5: Install in the user's project
Place the HTML form in the appropriate location in the codebase. The form
action URL points to RizzForms — no server-side code is needed.
For server-side/AJAX submissions, POST JSON to
https://forms.rizzness.com/json/{endpoint_token} instead.
Managing Existing Forms
CODEBLOCK7
Viewing Submissions
CODEBLOCK8
Spam Prevention
RizzForms has three layers of spam protection:
- 1. Honeypot fields — Hidden
_hp (or _gotcha) field. Bots fill it, submission gets marked as spam. Always include this in your HTML. - Turnstile CAPTCHA — Cloudflare Turnstile invisible challenge, enabled in the dashboard.
- Rate limiting — 60 submissions per minute per IP per form. Returns HTTP 429 when exceeded.
Special Fields
These field names get automatic normalization (stored in special_normalized):
| Field | Normalization |
|---|
| INLINECODE35 | Whitespace trimmed |
| INLINECODE36 , INLINECODE37 |
Whitespace trimmed |
|
name | Auto-computed from firstName + lastName if both present |
|
tags | CSV string converted to array |
|
priority | Lowercased, validated: low/medium/high/urgent |
|
urgent | Coerced to boolean |
|
_optin | Coerced to boolean (marketing opt-in) |
All fields are always stored as-is in payload_json regardless of normalization.
Error Handling
All errors return {"ok": false, "error": "code", "message": "..."}.
| Status | Error | Meaning |
|---|
| 401 | INLINECODE45 | API key missing, invalid, or expired |
| 403 |
forbidden | Key lacks required permission |
| 404 |
not_found | Form/plugin not found or wrong account |
| 404 |
not_active | Form is deactivated — reactivate with
--active true |
| 422 |
unsupported_plugin | Only "webhook" type supported via API |
| 422 |
invalid_config | Webhook URL invalid, not HTTPS, or private IP |
| 503 |
service_unavailable | Temporary — retry shortly |
Full API Reference
For complete endpoint details, webhook signing verification code, and framework
quick-starts (Next.js, Astro, Hugo), see references/api.md.