SAPI Documentation
Add visitor sessions, form handling, and temporary data storage to your AI-built websites. No login required — sessions are anonymous and cookie-based.
Overview
SAPI provides four capabilities for visitor interactions:
| Feature | Prefix | Description |
|---|---|---|
| Sessions | /sapi/project/{id}/session |
Anonymous cookie-based sessions with CSRF tokens |
| Data Store | /sapi/project/{id}/data |
Key-value storage per session (cart, preferences, state) |
| Form Config | /sapi/project/{id}/forms |
Define forms and their server-side actions (owner only) |
| Form Submit | /sapi/project/{id}/form/submit |
Anonymous form submission with CSRF validation |
How It Works
Session Lifecycle
When a visitor loads your website, a JavaScript snippet calls the session endpoint. The server creates a session in Redis with a 1-hour TTL, sets an httpOnly cookie, and returns a CSRF token. The session extends automatically when data is written (up to 24 hours max).
Authentication Model
SAPI uses two different authentication models:
| Who | Auth Method | Endpoints |
|---|---|---|
| Visitors (anonymous) | Session cookie (wss_) + CSRF token |
Session, Data Store, Form Submit |
| Owners (you / AI) | Bearer token (wps_ or wpa_) |
Form Configuration |
X-CSRF-Token header or _csrf in the request body.
Sessions
Start a new session or resume an existing one. Returns a session cookie and CSRF token.
Response
{
"success": true,
"data": {
"session_id": "wss_a1b2c3d4e5f6...",
"csrf_token": "f7e8d9c0b1a2...",
"expires_in": 3600,
"is_new": true
}
}
Response Headers
Set-Cookie: wss_session=wss_a1b2c3d4...; HttpOnly; Secure; SameSite=Lax; Path=/sapi/project/{project_id}/; Max-Age=3600
is_new is false, an existing session was resumed.
Destroy the current session. Clears all session data and the cookie.
Requires: session cookie.
Generate a new CSRF token. Invalidates the previous token.
Requires: session cookie.
Response
{
"success": true,
"data": {
"csrf_token": "new_token_here...",
"expires_in": 3600
}
}
Data Store
Store key-value data per visitor session. Ideal for shopping carts, user preferences, wizard state, and other temporary data. All data is stored in Redis and expires with the session.
Get all stored key-value data for the current session.
Requires: session cookie.
Response
{
"success": true,
"data": {
"cart_items": [{ "product_id": 42, "quantity": 2 }],
"cart_total": 29.98
}
}
Get a specific key from the session data store.
Requires: session cookie.
Set multiple keys at once. Existing keys are overwritten.
Requires: session cookie + CSRF token.
Request Body
{
"cart_items": [{ "product_id": 42, "quantity": 2 }],
"cart_total": 29.98
}
Response
{
"success": true,
"data": {
"keys_updated": ["cart_items", "cart_total"],
"session_expires_in": 14400
}
}
Set or update a single key in the session data store.
Requires: session cookie + CSRF token.
Delete a key from the session data store.
Requires: session cookie + CSRF token.
Forms
Forms have two sides: configuration (you, the owner) and submission (your visitors). You define which forms exist, what fields they require, and what action the server takes when a form is submitted. Visitors only see the form name and send their field values.
Form Configuration (Authenticated)
These endpoints require a Bearer token (wps_ or wpa_). Use them directly or through MCP tools (configure_form, list_forms, remove_form).
Create or update a form definition. Defines required fields, the server-side action, and rate limiting.
Requires: Bearer token.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
form_name |
string | Yes | Unique name for this form (e.g., "contact", "newsletter") |
required_fields |
array | Yes | Fields the visitor must submit (e.g., ["name", "email", "message"]) |
action |
object | No | Server-side action to execute on submit (see below). Defaults to type: "none". |
max_submits_per_session |
integer | No | Max submissions per visitor session. Default: 3. |
Action Types
| Type | Description | Required Fields |
|---|---|---|
"none" |
Store submission only, no server-side action | — |
"iapi" |
Execute an integration proxy call (e.g., send email via Resend) | service, endpoint, input_template |
Example: Contact Form with Email
{
"form_name": "contact",
"required_fields": ["name", "email", "message"],
"action": {
"type": "iapi",
"service": "resend",
"endpoint": "send-email",
"input_template": {
"to": "you@example.com",
"subject": "Contact from {{fields.name}}",
"html": "From: {{fields.name}} ({{fields.email}})<br>{{fields.message}}"
}
},
"max_submits_per_session": 3
}
{{fields.name}} to insert visitor-submitted values into the action template. The server merges the template before executing the action.
List all configured forms for this project.
Requires: Bearer token.
Get a specific form configuration.
Requires: Bearer token.
Delete a form configuration.
Requires: Bearer token.
Form Submission (Anonymous)
This endpoint is called by visitors on your website. The form must be pre-configured by the owner.
Submit a form. Validates required fields, checks submission limits, and executes the configured action.
Requires: session cookie + CSRF token.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
form_name |
string | Yes | Name of the configured form |
fields |
object | Yes | Field values as key-value pairs |
Example Request
{
"form_name": "contact",
"fields": {
"name": "Jan Jansen",
"email": "jan@example.nl",
"message": "I have a question about your service."
}
}
Response (with IAPI action)
{
"success": true,
"data": {
"form_accepted": true,
"submits_remaining": 2,
"action_result": {
"status": "completed",
"service": "resend",
"endpoint": "send-email"
}
}
}
type: "iapi", the corresponding API key must be stored in the Vault (VAPI) first. Otherwise the submit will return an error.
Security
CSRF Protection
SAPI uses the Double Submit Cookie pattern. The session cookie is httpOnly (not accessible from JavaScript), but the CSRF token is returned in the API response. Your JavaScript must include the token on every write request.
How to Send the CSRF Token
| Method | How |
|---|---|
| HTTP Header | X-CSRF-Token: your_csrf_token |
| Request Body | "_csrf": "your_csrf_token" |
CORS
SAPI validates the Origin header against your project's domain. Requests from unauthorized origins are rejected. Subdomains of websitepublisher.ai are allowed automatically.
Rate Limiting
| Endpoint Group | Limit |
|---|---|
| Session start/resume | 30 requests/minute |
| Data read/write | 60 requests/minute |
| Form submission | 10 requests/minute |
Anti-Abuse
Maximum 5 concurrent sessions per IP address per project. Excessive session creation triggers automatic rate limiting.
Error Handling
All errors return a consistent format:
{
"success": false,
"error": {
"message": "Description of the error",
"code": 403
}
}
HTTP Status Codes
| Code | Meaning |
|---|---|
200 |
Success |
400 |
Bad Request — invalid input |
401 |
Unauthorized — invalid Bearer token (form config endpoints) |
403 |
Forbidden — missing or invalid CSRF token, or CORS origin rejected |
404 |
Not Found — form not configured, key not found |
422 |
Validation Error — missing required fields |
429 |
Rate Limited — too many requests or max submissions reached |
500 |
Server Error |
Complete Example
Here's the full flow for adding a contact form to your website:
# 1. Configure the form (as owner, via API or MCP tool)
curl -X POST "https://api.websitepublisher.ai/sapi/project/{project_id}/forms/configure" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"form_name": "contact",
"required_fields": ["name", "email", "message"],
"action": {
"type": "iapi",
"service": "resend",
"endpoint": "send-email",
"input_template": {
"to": "you@example.com",
"subject": "Contact from {{fields.name}}",
"html": "From: {{fields.name}} ({{fields.email}})<br>{{fields.message}}"
}
}
}'
# 2. Visitor starts a session (from your website JavaScript)
curl -c cookies.txt "https://api.websitepublisher.ai/sapi/project/{project_id}/session"
# → Returns: csrf_token in response body + wss_session cookie
# 3. Visitor submits the form
curl -b cookies.txt -X POST "https://api.websitepublisher.ai/sapi/project/{project_id}/form/submit" \
-H "Content-Type: application/json" \
-H "X-CSRF-Token: {csrf_token_from_step_2}" \
-d '{
"form_name": "contact",
"fields": {
"name": "Jan Jansen",
"email": "jan@example.nl",
"message": "Great website!"
}
}'
# → Email sent via Resend integration
# 4. Verify form config
curl "https://api.websitepublisher.ai/sapi/project/{project_id}/forms" \
-H "Authorization: Bearer YOUR_TOKEN"
POST /vapi/project/{id}/secrets with the Resend API key. See Vault documentation.
Powered by WebSumo