Build and manage Tally forms, questions, blocks, submissions, analytics, workspaces, folders, webhooks and organization members via the Tally API.
Tally ships in the w6w first-party pack. It declares 38 actions, 2 health checks, and the host runs its code in a sandbox that never sees the credential.
io.w6w.tallyTally is a form builder, and this app covers more of it than most form integrations can — not just reading submissions, but authoring the form itself. Create, update and delete forms and their blocks, retitle questions, and organize everything into workspaces and folders you can create, update and delete as well.
Read submissions as they come in, and pull five different analytics views for a form — metrics, visits, submission counts, response dimensions and drop-off — to see how it is actually performing, not just what came through. Manage the webhook subscriptions that notify your systems of new activity, replay a delivery that failed, and read the delivery log.
On the team side, list an organization’s members, remove one, and send, list or cancel an invitation. The one thing this app cannot do is create a submission — Tally has no API for that; responses arrive only through the hosted form itself.
Three routes to the same 38 actions. The Workflow tab is generated from Tally's own manifest and carries its real ids, so it is copy-pasteable; the Code and CLI examples are the same call for any action on any app, so every app-specific value in them is a blank you fill in.
analytics-get-dimensions Audience breakdowns for a form — source, browser, OS, device, country, city — each as value -> count.
analytics-get-drop-off Per-step drop-off for a form, with the funnel stats behind it.
analytics-get-metrics Headline analytics for a form over a period: visits, starts, completions, rate.
analytics-get-submissions Completed and partial submissions over time for a form.
analytics-get-visits Visits over time for a form, bucketed by the server's chosen interval.
block-update-many Replace a form's block array (and optionally its settings). Omitted blocks are DELETED.
folder-get-many List every folder in a workspace. Not paginated — returns the full list.
form-update Update a form's name, status, blocks or settings. Supplying `blocks` REPLACES the whole array — omitted blocks are deleted.
organization-invite-create Invite one or more email addresses to one or more workspaces.
organization-invite-get-many List an organization's pending invites. Not paginated.
organization-user-get-many List the members of an organization. Not paginated.
organization-user-remove Remove a member from an organization. Their API keys stop working along with their membership.
question-update Change a question's title. Only the title is editable through this endpoint.
submission-delete Delete a single submission. Tally moves it to trash rather than erasing it.
submission-get Retrieve a single submission, with the questions needed to label its responses.
submission-get-many List a form's submissions, filtered by completion state and date, with the question set needed to label the answers.
user-get Retrieve the authenticated user, including `organizationId` and `subscriptionPlan`.
webhook-event-get-many List a webhook's delivery attempts. Pages are fixed at 25 events.
webhook-update Update a webhook subscription, or pause it with `isEnabled: false`. The API requires the full form ID, URL, event types and enabled flag on every call.
workspace-get-many List the workspaces on this account, with their members, invites and folders.
A workflow step names the app and the action, and the editor fills in the
connection when you pick one. This is the Step shape from the
workflow spec, carrying Tally's real ids.
{
"manifestVersion": "2",
"name": "tally-example",
"steps": [
{
"id": "analytics-get-dimensions",
"uses": {
"app": "io.w6w.tally",
"action": "analytics-get-dimensions",
"connection": "conn_YOUR_CONNECTION_ID"
},
"with": {
"formId": "<formId>",
"period": "<period>"
}
}
]
}analytics-get-dimensions analytics-get-drop-off analytics-get-metrics analytics-get-submissions analytics-get-visits +33 more actions available
Every app-specific value here is a blank you have to fill in. An
app action is reached through the connection that authenticates it, so the
address is a connection id, not the app id — and connections belong to your account,
so a public page cannot know yours. Create one for Tally, then fill in
the three blanks: conn_YOUR_CONNECTION_ID, the action key, and the
parameters that action declares. The call itself is real — the shape is transcribed
from the studio's own snippet builder, which prints the same kind of blanks — but
nothing in it is specific to Tally. The Workflow tab is where this app's
real ids are.
npm install @w6w/sdkyarn add @w6w/sdkpnpm add @w6w/sdkdeno add npm:@w6w/sdkimport { W6wClient, isActionRun } from "@w6w/sdk";
// Reads W6W_BASE_URL and W6W_TOKEN from the environment when omitted.
const client = new W6wClient();
const envelope = await client.run({
urn: "conn_YOUR_CONNECTION_ID",
action: "form-create",
payload: {
blocks: "<value>",
status: "<value>",
// workspaceId: "<value>",
// templateId: "<value>",
// folderId: "<value>",
// settings: "<value>",
},
});
if (isActionRun(envelope)) console.log(envelope.value); npm install -g @w6w/cli w6w run conn_YOUR_CONNECTION_ID --action form-create --payload '{"blocks":"<value>","status":"<value>"}' Give an AI agent Tally — without giving it Tally's credentials. One MCP endpoint exposes every app, function and workflow the caller is entitled to, as tools it can discover and run. Access is granted per team while we onboard.
One tool call{
"name": "w6w_invoke",
"arguments": {
"ref": "app:io.w6w.tally#analytics-get-dimensions",
"input": {
"formId": "<formId>",
"period": "<period>"
}
}
}
Every tool names its target with a single ref. The
app: form above doesn't name a connection at all — the
host resolves which of the caller's Tally connections to sign
with, and refuses rather than guesses when the answer is ambiguous.
The token is attached host-side, at the moment of the call. It is never a tool argument, never in the model's context, and never in a transcript — so a prompt injection has nothing to exfiltrate.
Tools are derived per end user from what that person has actually connected and is entitled to — not one shared bot identity carrying the union of everyone's access.
Multi-step work runs on the workflow engine and returns a run handle the agent can poll — retries, branching and state survive the conversation that started them.
Tally's declared health checks are on the surface too, so an agent can tell "the vendor is down" from "your credential expired" before it burns a retry on either.
The MCP surface is part of the hosted platform. Tally itself is MIT, and the runtime that executes it is source-available (FSL).
Tally declares its own checks, so its health is a property of the app rather than something the host guesses at.
Better Stack status page for status.tally.so: the aggregate state plus per-component detail (Tally Application, Tally API, Custom domains). Unauthenticated and unsigned.