A clear path from
submission to decision.
Connect your backend to Spamadin. Keep your Spamadin API key on your server. Save submissions in your own system before making a classification request.
Build with your coding assistant
Give your coding assistant our integration reference and OpenAPI specification to build a server-side connection for your website.
Integration reference · OpenAPI specification · AI documentation index
WordPress plugin
Install Spamadin in WordPress and paste your account API key into Settings → Spamadin. Use the same key on every website in your plan. Connecting adds the website automatically within your plan’s limit and securely stores the key on your WordPress server.
Comments and supported contact forms are protected by default. Contact Form 7, WPForms, Gravity Forms, Fluent Forms and Elementor Pro are supported. You can change protection and optional browser evidence in the plugin settings.
Spam comments stay in WordPress’s spam queue; uncertain or unavailable checks go to moderation. Held form messages stay privately in Settings → Spamadin for up to 30 days. Review them there and contact legitimate senders directly; form actions are not replayed. Corrections share only a check reference and label, never the message body.
Disconnecting the plugin stops local checks. Block the website in your Spamadin dashboard to stop its API requests and free its website slot. Reconnecting cannot unblock it. Rotating your account key requires updating every integration.
1. Create an account and key
Verify your email, choose a subscription in the dashboard, and create one account API key. Reuse it across all your websites. Each allowed hostname uses one website slot; example.com and www.example.com count separately. Keys are shown once and stored using Argon2id.
Your Spamadin key authenticates requests to this service and is separate from our internal AI provider credentials. Your subscription includes classification; you do not need an OpenRouter account or key.
2. Check a submission
Describe your website when connecting it. Spamadin uses AI to categorize it and includes that context in future checks. Disable a website in your dashboard to stop its requests and free a website slot. After a downgrade, the oldest enabled websites within your plan limit stay active; disable an older website to enable another.
POST /api/v1/check · JSON body · Authorization: Bearer YOUR_SERVER_KEY
curl https://spamadin.com/api/v1/check \
-H 'Authorization: Bearer YOUR_SERVER_KEY' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: 45573012-1f18-4eaf-97a1-29702376ea21' \
-d '{
"type": "contact",
"siteUrl": "https://example.com",
"content": "Can you send a quote for our new website?",
"context": { "title": "Website design", "language": "en" },
"signals": { "elapsedMs": 8500, "honeypotFilled": false }
}'Bulk checks
Send up to 20 submissions to POST /api/v1/check/bulk using your account API key. Items can belong to different websites in your account. Each item needs a unique UUID id and a submission object. Responses preserve order and include each item’s status and result. Retry with the same id and unchanged submission. Website limits, blocks and the shared monthly allowance apply to every item.
{
"checks": [
{
"id": "45573012-1f18-4eaf-97a1-29702376ea21",
"submission": {
"type": "contact",
"siteUrl": "https://example.com",
"content": "Can you send a quote?"
}
}
]
}Form-specific context
Create a form profile in your dashboard using its purpose, field labels, and relevant public text. Review and save the suggested summary, then include its formId in single or bulk checks. Use reader_comment profiles with comment submissions and other profiles with contact submissions. Profiles provide context, not an automatic permission to send promotional messages.
{
"formId": "d495b23a-cf83-4c3d-bd63-6973297ec401",
"type": "contact",
"siteUrl": "https://example.com",
"content": "Can you send a quote?"
}Use a fresh UUID Idempotency-Key per submission. Retry the same payload with the same key after network errors. A completed retry returns the existing decision without a second charge. Keys are retained with check metadata for 30 days; do not retry older submissions.
| Field | Purpose | Limit |
|---|---|---|
| content | Original message text; required | 1–12,000 characters |
| type | contact or comment; required | Exact enum |
| siteUrl | Website URL; required. New sites register automatically within your plan limit. | HTTP(S), 2,048 characters |
| context.title / description | Relevant page context provided by your server | 200 / 1,000 characters |
| context.language / tags | Site language and up to ten topic tags | 35 / 50 characters |
| signals.elapsedMs | Time between form display and submission | 0–86,400,000 ms |
| signals.honeypotFilled | Whether a hidden field was filled | Boolean |
| signals.userAgent / userIp | Optional integration metadata; not sent to models | 512 characters / valid IP |
Derive context and behavior on your server. Browser timestamps can be forged. For future CMS plugins, sign the form-render timestamp and validate it on submission, use a honeypot with an accessibility-safe implementation, and hook into the CMS’s server-side comment/form lifecycle. Do not forward cookies, authorization headers, passwords, full environment variables, or other secrets.
3. Preserve the uncertain
{
"id": "c18dd105-5d52-4939-a63e-0d52b2c0606d",
"verdict": "allow",
"score": 0,
"reasons": ["clear_legitimate_context"],
"degraded": false,
"latencyMs": 420,
"charged": true
}This is an illustrative response, not a latency guarantee. Score is an ordinal risk indicator: 0 (allow), 50 (review), or 100 (strong spam agreement). It is not a calibrated probability.
- allow: proceed with normal moderation or delivery.
- review: preserve the submission in a review queue. Never discard it.
- spam: quarantine it. Keep a recovery path for false positives.
- degraded: true: a provider failed or returned invalid output. Preserve for review. This check is uncharged.
Automatic spam classification starts in shadow mode until the operator enables an evaluated blocking policy. If your network call fails or returns any non-2xx status, save for review rather than discarding or retrying indefinitely.
4. Report a correction
POST /api/v1/feedback with the same Bearer API key and JSON {"id":"CHECK_UUID","label":"legitimate"} or label spam. Feedback is restricted to checks created by that key. It records a correction for evaluation; it does not modify shared training or immediately whitelist a sender.
You can also record a correction in your dashboard and contact support with the check reference. We can adjust context and caution for your website. Do not email private submission contents, passwords, or API keys. Check references remain available for 30 days.
Limits and error handling
- Body maximum: 32 KiB. Unknown fields and malformed JSON are rejected.
- 120 requests per key per minute. Concurrency is capped per app process; excess checks return 503.
- 400 invalid input, 401 invalid/revoked key, 402 inactive subscription, 409 pending or mismatched retry, 413 oversized body, 429 rate/allowance limit, 503 temporary capacity.
- Retry transient errors with bounded exponential backoff. Reuse the Idempotency-Key and honor Retry-After when present.
- Monthly quotas are shared across all keys and reset on the monthly anniversary of the subscription anchor, including annual plans. Dates clamp to the last day in shorter months. No rollovers or automatic overages.
Report a classification mistake
Report a genuine message marked as spam to POST /api/v1/reports/false-positive, or missed spam to POST /api/v1/reports/missed-spam. Send only the returned check ID using your account API key. Reports share no message content and use no spam checks. The latest correction wins; reports do not instantly retrain a model.
{
"id": "c18dd105-5d52-4939-a63e-0d52b2c0606d"
}Privacy by default
Routine checks retain metadata and a keyed input digest for 30 days, without storing message bodies. Common email and phone patterns are redacted before AI processing; this is minimization, not complete anonymization. Voluntarily shared training examples are stored separately under the privacy policy.
Optional invisible form checks
Call POST /api/v1/form-token from your server with siteUrl, type, and an optional saved formId. Tokens expire after 30 minutes. Issuing a token does not consume a spam check and requires active website access.
Send the returned token in signals.formToken and use the returned idempotencyKey as your Idempotency-Key header. In bulk checks, use it as the item id. Retries must preserve the original request exactly. Use a new token for a new submission.
Load /spamadin-behavior.js on your website and attach it to your form. Your same-origin token endpoint calls Spamadin from your server and returns only the public form token response. Never expose your API key or embed shared tokens in cached pages.
SpamadinBehavior.attach(document.querySelector("#contact-form"), {
tokenEndpoint: "/form-evidence/token"
});The helper adds a hidden spamadin_evidence field containing token, idempotencyKey, and browser evidence. Validate that field on your server, map browser to signals.browser, and forward token as signals.formToken. Browser evidence uses version 1, jsExecuted, focusCount, editCount, pasteCount, keyboardUsed, pointerUsed, and optional firstInteractionMs. Your server may also send honeypotFilled and the visitor IP.
These signals support classification; they never establish spam by themselves. Pasting, autofill, missing JavaScript, and assistive technology can all be legitimate. Timing measures time since token issuance, not proof of human activity. Missing or expired tokens do not automatically reject messages.
The helper does not record typed text, clipboard contents, mouse trails, or cookies. Tokens and interaction summaries are not stored in check history or sent as credentials to AI providers. Checks with browser evidence always run classification rather than reusing a cached verdict.
Repeat recognition
Repeat recognition is scoped to each website. Fingerprints expire after 24 hours and contain no saved message text. Similar wording supports classification; it never establishes spam on its own. Turn recognition off in website settings to clear its history and approved campaign signals. Blocking a website or reporting a correction also clears its recent fingerprint history.
Eligible identical checks may reuse a recent genuine-message verdict only when that feature has been evaluated and enabled. Reuse is limited to five minutes and requires unchanged submission data, website and form settings, model configuration, and detection policy. Fuzzy matches never bypass AI. Every completed check still counts once toward your allowance, including a reused verdict; failed provider checks remain uncharged.
Share a corrected training example
POST /api/v1/training with the original key, checkId, corrected label, unchanged original submission object, and both sharing acknowledgments set to true. The check must be completed and less than 30 days old. Sharing is optional, does not consume a spam check, and does not immediately retrain a model. Keep this request on your backend and provide the required notices and lawful authority before sharing visitor content.
{
"checkId": "c18dd105-5d52-4939-a63e-0d52b2c0606d",
"label": "legitimate",
"submission": {
"type": "contact",
"siteUrl": "https://example.com",
"content": "Can you send a quote?"
},
"consent": {
"authorizedToShare": true,
"useForSpamImprovement": true
}
}GET /api/v1/training lists your account’s shared-example references. Use ?after=EXAMPLE_UUID for the next page or ?id=EXAMPLE_UUID to retrieve a minimized example. DELETE the same endpoint with the example id to withdraw it. Your dashboard also lets you withdraw examples after your account key is revoked.
Examples expire after 90 days. Limits: 500 examples or 2 MiB of encrypted payload per account, 16 KiB of original message text per example, and 24 KiB of minimized content and context before compression. Requests remain limited to 32 KiB. A full account returns 429; temporary platform capacity or unavailable encryption returns 503. No example is stored when a limit rejects the request. Do not retry indefinitely or send private attachments. Redaction is best-effort, not anonymization.
The API never visits submitted URLs. Optional visitor IPs support short-term, website-scoped burst evidence; user agents do not affect classification. Neither is sent to models. Use our WordPress plugin or connect other CMS platforms through the server API.
Create your account ↗