For developers
Build with Votinova
REST API, webhooks, connectors and an MCP server. Everything the web app does is also in the API: create a session, open it and get results into your stack in under five minutes.
- Create a credential in your workspace — two scopes, organization-wide.
- Make your first call: create and open a session from curl.
- Subscribe to a webhook and receive results as each question closes.
curl -X POST https://api.votinova.dev.atbionapps.com/public/v1/sessions \ -H "X-API-Key: vz_live_…" \ -d '{ "presentation_id": "prs_8f3k2" }' 200 OK · X-RateLimit-Remaining: 119{ "id": "ses_71xw9", "join_code": "482913", "state": "LIVE"}Every surface, one contract
Every integration derives from the same OpenAPI document. A new endpoint shows up everywhere at once.
REST API
Public endpoints with scopes, per-plan quotas and cursor pagination.
API reference →Webhooks
HMAC-signed events with backoff retries and manual redelivery.
Webhooks guide →Zapier
Triggers and actions to automate with no code.
View integration →Power Automate
Webhook-trigger connector for the Microsoft ecosystem.
View integration →MCP server
Tools derived from the OpenAPI document for your agents. Remote, nothing to install.
Set up MCP →Add-ins
PowerPoint, Google Slides, Teams, Zoom and Webex.
View add-ins →What people build with this
Results in your CRM
Sync answers and attendance into your CRM or data warehouse the moment each question closes — a webhook tells you, no polling.
Your own reporting
KPIs, participants and results over the API with cursor pagination: build the report exactly the way your organization wants it.
Sessions from your software
Create, open and drive sessions from your backend, your internal tools or your agent — the same API this web app uses.
Your first call, in your language
Full quickstart →Built for agents
Your agent already knows Votinova
A remote MCP server with tools derived from the OpenAPI document, filtered by your credential's scopes. Plus an llms.txt so any model can find the docs.
Set up the MCP server →{ "mcpServers": { "votinova": { "url": "https://mcp.votinova.com/mcp", "headers": { "Authorization": "Bearer vz_live_…" } } }}Latest API changes
Changelog →- ADDITIVE
An event stranded by an auto-disable is now dead-lettered, so it can be replayed once the endpoint is back. Recovering from a bad afternoon is supposed to be: the platform pauses the endpoint, you fix it, you re-enable it, you redeliver what never arrived. Redelivery only accepts a dead-lettered delivery, and the queue of an auto-disabled endpoint was being terminated as merely failed — so there was nothing to replay and nothing said so. The arithmetic hid it: a delivery gets 8 attempts and an endpoint is paused after 10 consecutive failures, so a single event in flight always dead-letters first. The counter is per endpoint, and one ordinary session emits three events. Deliveries for an endpoint its owner REMOVED are still terminated without a dead letter: nobody is coming back for those.
- ADDITIVE
Three corrections found by driving this API the way an integration does. A bad request body now answers the envelope this surface documents ({ error, code: INVALID_REQUEST_BODY, invalid_fields }) naming the PUBLISHED field: it used to fall through to a shape the API emits nowhere else, which named the internal property (TargetUrl for target_url) and carried no error member for a generated client to read. session.ended is delivered ONCE per close; closing a live session through this API delivered it twice, so anything that posts a summary or books a room did it twice, and the event now always names presentation_id whichever surface closed the session. And enabling an endpoint only revives one the platform paused after failures: it used to undo a DELETE too, so a connector's unsubscribe could be reversed by one call while the connector itself could no longer remove it. Enabling an endpoint that was removed now answers 400 WEBHOOK_NOT_AUTO_DISABLED; register it again instead.
- ADDITIVE
question_id is now published as REQUIRED on per-question results, which is what the endpoint has always enforced: it answers 400 without one. The document said optional, so an integrator who believed it did not send the value and found out from a failed call — and the failed call counted against the organization's quota. Nothing about the endpoint's behaviour changed, and no request that worked before stops working; what changed is that the reference now describes it. Generated clients and MCP tools derived from this document will ask for the question up front instead of discovering the rule at runtime.
Quota per plan
- STARTER120 requests every 60s
- PRO120 requests every 60s
- TEAM600 requests every 60s
- ENTERPRISE3000 requests every 60s
Your quota travels in every response (X-RateLimit-* headers).