Inkwellportfolio demo

Form submission · explainable spam scoring · multi-destination fan-out

Drop one HTML form tag. Inkwell handles the rest — spam, fan-out, replay, retention.

The integration is a plain <form action="…">. No JavaScript required. The backend scores submissions with a transparent signal breakdown, validates against a JSON Schema you control, and fans out to email + webhook + Slack + Discord + Google Sheets + HubSpot + Mailchimp — configured per form, dispatched in parallel, retried on failure.

The canonical integration

Paste this into your HTML. That's the integration. Works on static sites, in HTML emails, inside iframes, with JavaScript off, with screen readers. The 3 KB optional widget adds inline error rendering and honeypot timing without changing the canonical path.

HTML — no JS required
<form action="https://api.inkwell.philiprehberger.com/v1/forms/<form-id>/submit"
      method="post">
  <input name="name" required>
  <input name="email" type="email" required>
  <textarea name="message" required></textarea>

  <input type="hidden" name="_redirect" value="https://example.com/thanks">
  <input type="text"   name="_subject_honeypot" style="display:none">

  <button type="submit">Send</button>
</form>
curl — for server-side senders
curl -X POST https://api.inkwell.philiprehberger.com/v1/forms/<id>/submit \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{"name":"Alice","email":"a@example.com","message":"hi"}'

What's in the box

Explainable spam scoring

Every submission shows the per-signal breakdown in the admin — honeypot, IP reputation, timing, content density, email validity, captcha. Buyers see why something scored what it scored, and adjust thresholds with that signal in view. No black box.

Graduated friction, not binary block

Submissions in the 30–49 score band don't get rejected outright — they're quarantined and a captcha challenge releases them. 50+ becomes spam (no destinations dispatched). Honeypot or known-bad IP hard-blocks immediately.

Per-destination fan-out you can trust

Each destination is its own Horizon job with a (submission, destination, replay_seq) idempotency key — worker crashes never double-send. Slow OAuth connectors run on a separate queue from webhook/email/Slack so backpressure doesn't compound.

Compliance hooks ready

POST /v1/data-subjects/lookup finds every submission containing an email. DELETE /v1/data-subjects/by-email queues an async erasure that cascades through submissions, files, delivery attempts, and audit-row redaction. PII-purge cron runs at the 90-day mark. Production-shaped, not production-grade.

Webhook signing, secret rotation with grace

Webhook destinations sign payloads with HMAC-SHA256 in Stripe's t=…,v1=…format. Rotating a destination's secret keeps the old signature valid for 48 hours alongside the new one — your receivers update without dropping anything in flight.

File uploads, scanned + EXIF-stripped

Magic-byte verification against your form's MIME allowlist. Images pass through EXIF strip (GPS coordinates removed). ClamAV scan runs out-of-band — submissions acknowledge fast, infected files quarantine + flip the submission to quarantined, buyer reviews from the admin.

Destinations

Configure any combination per form. Each delivery is independent — a failed Slack notification doesn't block the webhook fan-out.