Tool reference

Forms tools

All 15 forms MCP tools with input shape, response shape, and example calls.

Fifteen tools cover forms, their submissions, and webhook delivery — the whole Web3Forms-style pipeline: define a form, accept submissions, notify or reply, and export.

All endpoints follow the pattern POST https://mcp.vlozi.app/tools/forms.<name> with Authorization: Bearer ls_xxx. All responses use this envelope:

{ "data": <payload>, "error": null }   // success
{ "data": null,      "error": "..." }  // failure (with HTTP 4xx/5xx)

NOTE

Every accepted (non-spam) submission costs 0.25 credit, metered as you go and settled to the ledger in whole-credit increments — submitting is blocked with 402 once your balance can't cover it. File-field uploads are proxied through the media service; a file over 25MB or 5 files rejects the whole submission, while a transient media-service failure fails soft (the filename is kept, the file isn't).

Forms

forms.list_forms

List forms in the workspace.

Scope: forms:read

Input: none

Response: { forms: { id, name, slug, status, captchaRequired, submissionCount, createdAt, updatedAt }[] }


forms.get_analytics

Submission analytics — a workspace overview, or one form's detail.

Scope: forms:read

Input: { formId?: string, days?: 7 | 30 | 90 } (default 30; anything else is coerced to 30)

Response (no formId): { counts: { total, new, seen, handled, spam }, deltas: {...}, byForm: { id, name, count }[], series: { date, count }[], views: number, conversion: number | null, usage: { used, limit: -1 }, credits: { balance, spentThisMonth, perSubmission: 0.25, pending } | null }

Response (with formId): { form: { id, name }, counts: {...}, totals: { views, submissions, conversion }, previous: { views, submissions }, series: { date, views, submissions }[], days }

NOTE

usage.limit always reports -1 (unlimited) in this payload even on plans with a real monthly submission cap — the cap is enforced elsewhere, this overview just doesn't reflect it yet.


forms.get_form

Get a form's full definition, including its webhook signing secret.

Scope: forms:read

Input: { id: string } (required)

Response: { form: { id, name, slug, schema, settings, notify, webhookUrl, webhookSecret, allowedOrigins, honeypotField, captchaRequired, status } }


forms.create_form

Create a new form.

Scope: forms:write

Input:

Field Type Notes
name string (required) 1–200 chars
schema { fields: FormField[] } Omit/empty to accept any posted fields
settings { successMessage?, redirectUrl?, theme?, turnstileSiteKey? }
notify { email?: { enabled, to? }, newsletterListId? }
webhookUrl string
allowedOrigins string[] Max 50
honeypotField string Default "botcheck"
captchaRequired boolean Default false

Response: { form: object } — a webhookSecret is generated automatically if webhookUrl is set

WARNING

captchaRequired: true is rejected unless settings.turnstileSiteKey is also provided — otherwise every submission would fail captcha silently. webhookUrl can't point at api.vlozi.app (self-loop guard).


forms.update_form

Update a form. Pass only the fields to change.

Scope: forms:write

Input: { id: string } plus any subset of create_form's fields, plus status?: "active" | "paused" | "archived"

Response: { form: object }

NOTE

Setting a webhookUrl for the first time mints a fresh webhookSecret.


forms.delete_form

Archive a form.

Scope: forms:write

Input: { id: string } (required)

Response: { id: string, status: "deleted" }

NOTE

Soft delete — existing submissions are kept, just the form itself stops accepting new ones.


forms.duplicate_form

Clone a form's configuration into a new one named "<original> (copy)".

Scope: forms:write

Input: { id: string } (required)

Response: { form: object } — the new form, with its own id/slug/webhook secret

Submissions

forms.list_submissions

List submissions across all forms (the global inbox) or one form.

Scope: forms:read

Input: { status?: "new" | "seen" | "handled" | "spam", formId?: string, q?: string, page?: number, limit?: number } (limit default 25, max 100)

Response: { data: object[], meta: { page, limit, total, totalPages, counts: { all, new, seen, handled, spam } } }

NOTE

q searches submission data and internal notes. counts always reflects the other filters but ignores status, so tab counts show what you'd see clicking each tab.


forms.get_submission

Get one submission.

Scope: forms:read

Input: { id: string } (required)

Response: { submission: { id, formId, data, meta, status, spamReason, note, sourceIp, userAgent, submittedAt, handledAt } }

NOTE

File-field values appear as { __file: true, mediaId, name, size, type, url }.


forms.update_submission

Update a submission's status or note.

Scope: forms:write

Input: { id: string, status?: "new" | "seen" | "handled" | "spam", note?: string } (at least one of status/note required)

Response: { submission: { id, formId, status, note, handledAt } }

NOTE

Setting status: "handled" stamps handledAt; any other status clears it.


forms.delete_submission

Permanently delete a submission.

Scope: forms:write

Input: { id: string } (required)

Response: { id: string, status: "deleted" }

CAUTION

Hard delete — no restore, unlike blog posts.


forms.reply_submission

Email a reply to whoever submitted the form.

Scope: forms:write

Input: { id: string, subject?: string, message: string } (message required; subject defaults to "Re: your submission")

Response: { sent: true }

IMPORTANT

Requires the submission to have a plain-string email field — returns an error if it doesn't. Sends through the comms service (real email, not a webhook), and marks the submission handled on success. Returns 502 if the send fails, including if the recipient is on your suppression list.


forms.export_submissions

Export every submission for a form.

Scope: forms:submissions.export

Input: { formId: string } (required)

Response: { submissions: object[] } — all submissions, oldest first, as inline JSON

NOTE

This returns JSON directly in the response, not a file URL or CSV — for a CSV export, use the dashboard's own export button instead.

Webhooks

forms.list_webhook_deliveries

List recent webhook delivery attempts for a form.

Scope: forms:read

Input: { formId: string } (required)

Response: { deliveries: { id, submissionId, url, attempt, statusCode, responseBody, status: "pending" | "success" | "failed" | "retrying", deliveredAt, createdAt }[] } — most recent 20


forms.retry_webhook_delivery

Retry a failed webhook delivery.

Scope: forms:write

Input: { id: string } (required — the delivery id, not the submission id)

Response: { retried: true }

NOTE

Redelivers to the submission using the form's current webhook config (up to 3 attempts with backoff) — if the webhook URL has since been removed, this fails with "This form has no webhook URL". Each retry writes a new delivery record rather than overwriting the original failed one.

MCP · Tool referenceEdit on GitHub