{"openapi":"3.1.0","info":{"title":"meetlark.ai API","version":"1.0.0","description":"Agent-first poll orchestration API. Create time-based polls, collect availability votes from participants, and manage poll lifecycle. Designed for programmatic use by AI agents and integrations — all endpoints return structured JSON with consistent envelope format."},"components":{"schemas":{},"parameters":{}},"paths":{"/api/v1/polls":{"post":{"description":"Create a new scheduling poll with a title and a set of candidate time slots. Returns admin and participation tokens — share the participation URL with voters and use the admin token to manage the poll lifecycle.","parameters":[{"schema":{"type":"string","description":"When true and email is unverified, automatically send a verification email","example":"true"},"required":false,"description":"When true and email is unverified, automatically send a verification email","name":"autoVerify","in":"query"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string","minLength":1,"description":"Poll title","example":"Team standup time"},"description":{"type":"string","description":"Additional context for participants","example":"Find a time for daily standup"},"timeSlots":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string","minLength":1,"description":"Date in YYYY-MM-DD format","example":"2026-02-10"},"startTime":{"type":"string","minLength":1,"description":"Start time in HH:MM format","example":"09:00"},"endTime":{"type":"string","minLength":1,"description":"End time in HH:MM format","example":"09:30"}},"required":["date","startTime","endTime"],"description":"Time slot input for poll creation","example":{"date":"2026-02-10","startTime":"09:00","endTime":"09:30"}},"minItems":1,"description":"Available time slots to vote on"},"creatorEmail":{"type":"string","format":"email","description":"Creator email for notifications","example":"creator@example.com"},"creatorName":{"type":"string","description":"Creator name for personalized emails","example":"Alice"}},"required":["title","timeSlots","creatorEmail"],"description":"Input for creating a new poll"}}}},"responses":{"201":{"description":"Poll created successfully","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","description":"Poll ID","example":"abc123"},"adminToken":{"type":"string","description":"Admin token","example":"adm_abc123"},"adminUrl":{"type":"string","description":"Absolute URL for admin","example":"http://localhost:3000/admin/adm_abc123"},"participateToken":{"type":"string","description":"Participate token for sharing","example":"prt_abc123"},"participateUrl":{"type":"string","description":"Absolute URL for participation","example":"http://localhost:3000/vote/participate/prt_abc123"}},"required":["id","adminToken","adminUrl","participateToken","participateUrl"]},"meta":{"type":"object","properties":{"requestId":{"type":"string","description":"Unique request identifier","example":"req_abc123"}},"required":["requestId"],"description":"Response metadata"}},"required":["data","meta"],"description":"Standard success response envelope"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Machine-readable error code","example":"validation_error"},"message":{"type":"string","description":"Human-readable error message","example":"Title is required"}},"required":["code","message"],"description":"Error detail"},"meta":{"type":"object","properties":{"requestId":{"type":"string","description":"Unique request identifier","example":"req_abc123"}},"required":["requestId"],"description":"Response metadata"}},"required":["error","meta"],"description":"Error response envelope"}}}},"403":{"description":"Email not verified. If autoVerify=true was passed, includes details about the verification email sent.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Machine-readable error code","example":"email_not_verified"},"message":{"type":"string","description":"Human-readable error message"},"details":{"type":"object","properties":{"verificationSent":{"type":"boolean","description":"Whether a verification email was sent"},"email":{"type":"string","format":"email","description":"Email address that needs verification"}},"required":["verificationSent","email"]}},"required":["code","message","details"]},"meta":{"type":"object","properties":{"requestId":{"type":"string","description":"Unique request identifier"}},"required":["requestId"]}},"required":["error","meta"]}}}}}}},"/api/v1/polls/admin/{token}":{"get":{"description":"Retrieve full poll details using the admin token. Returns everything in the participant view plus admin-only fields (admin token, admin URL, poll ID). Use this to check poll status before closing or reopening.","parameters":[{"schema":{"type":"string","description":"Admin token for the poll","example":"adm_abc123"},"required":true,"description":"Admin token for the poll","name":"token","in":"path"}],"responses":{"200":{"description":"Full poll data","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","description":"Unique poll identifier","example":"abc123"},"title":{"type":"string","description":"Poll title","example":"Team standup time"},"description":{"type":"string","description":"Additional context for participants","example":"Find a time for daily standup"},"adminToken":{"type":"string","description":"Secret token for admin operations (close, reopen)","example":"adm_abc123"},"adminUrl":{"type":"string","description":"Admin dashboard URL — do not share with participants","example":"http://localhost:3000/admin/adm_abc123"},"status":{"type":"string","enum":["open","closed"],"description":"Current status of the poll","example":"open"},"createdAt":{"type":"string","description":"ISO 8601 creation timestamp","example":"2026-02-01T12:00:00Z"},"timeSlots":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Unique time slot identifier","example":"ts_abc123"},"date":{"type":"string","description":"Date in YYYY-MM-DD format","example":"2026-02-10"},"startTime":{"type":"string","description":"Start time in HH:MM format","example":"09:00"},"endTime":{"type":"string","description":"End time in HH:MM format","example":"09:30"}},"required":["id","date","startTime","endTime"],"description":"Time slot with ID","example":{"id":"ts_abc123","date":"2026-02-10","startTime":"09:00","endTime":"09:30"}},"description":"Available time slots to vote on"},"responses":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Unique response identifier","example":"resp_abc123"},"name":{"type":"string","nullable":true,"description":"Participant name (null if not yet set)","example":"Alice"},"votes":{"type":"object","additionalProperties":{"type":"string","enum":["yes","maybe","no"],"description":"Availability vote for a time slot","example":"yes"},"description":"Votes keyed by time slot ID","example":{"ts_001":"yes","ts_002":"maybe"}},"submittedAt":{"type":"string","description":"ISO 8601 timestamp","example":"2026-02-01T12:00:00Z"}},"required":["id","name","votes","submittedAt"],"description":"A participant's vote response"},"description":"All submitted votes"},"participants":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Unique participant identifier","example":"part_abc123"},"name":{"type":"string","nullable":true,"description":"Participant name","example":"Alice"},"email":{"type":"string","nullable":true,"description":"Participant email (null for legacy participants)","example":"alice@example.com"},"votedAt":{"type":"string","nullable":true,"description":"ISO 8601 timestamp when participant voted","example":"2026-02-01T14:00:00Z"}},"required":["id","name","email","votedAt"],"description":"Participant in a poll with status information"},"description":"All participants invited to the poll"}},"required":["id","title","description","adminToken","adminUrl","status","createdAt","timeSlots","responses","participants"],"description":"Full admin view of a poll — includes admin token and URL"},"meta":{"type":"object","properties":{"requestId":{"type":"string","description":"Unique request identifier","example":"req_abc123"}},"required":["requestId"],"description":"Response metadata"}},"required":["data","meta"],"description":"Standard success response envelope"}}}},"404":{"description":"Poll not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Machine-readable error code","example":"validation_error"},"message":{"type":"string","description":"Human-readable error message","example":"Title is required"}},"required":["code","message"],"description":"Error detail"},"meta":{"type":"object","properties":{"requestId":{"type":"string","description":"Unique request identifier","example":"req_abc123"}},"required":["requestId"],"description":"Response metadata"}},"required":["error","meta"],"description":"Error response envelope"}}}}}}},"/api/v1/polls/admin/{token}/close":{"post":{"description":"Close an open poll so it no longer accepts votes. Use this once enough responses have been collected and a decision can be made. Requires the admin token. Fails with 409 if the poll is already closed.","parameters":[{"schema":{"type":"string","description":"Admin token for the poll","example":"adm_abc123"},"required":true,"description":"Admin token for the poll","name":"token","in":"path"}],"responses":{"200":{"description":"Poll closed successfully","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"success":{"type":"boolean","description":"Whether the poll was closed"}},"required":["success"]},"meta":{"type":"object","properties":{"requestId":{"type":"string","description":"Unique request identifier","example":"req_abc123"}},"required":["requestId"],"description":"Response metadata"}},"required":["data","meta"],"description":"Standard success response envelope"}}}},"404":{"description":"Poll not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Machine-readable error code","example":"validation_error"},"message":{"type":"string","description":"Human-readable error message","example":"Title is required"}},"required":["code","message"],"description":"Error detail"},"meta":{"type":"object","properties":{"requestId":{"type":"string","description":"Unique request identifier","example":"req_abc123"}},"required":["requestId"],"description":"Response metadata"}},"required":["error","meta"],"description":"Error response envelope"}}}},"409":{"description":"Poll is already closed","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Machine-readable error code","example":"validation_error"},"message":{"type":"string","description":"Human-readable error message","example":"Title is required"}},"required":["code","message"],"description":"Error detail"},"meta":{"type":"object","properties":{"requestId":{"type":"string","description":"Unique request identifier","example":"req_abc123"}},"required":["requestId"],"description":"Response metadata"}},"required":["error","meta"],"description":"Error response envelope"}}}}}}},"/api/v1/polls/admin/{token}/reopen":{"post":{"description":"Reopen a previously closed poll to resume collecting votes. Use this if more responses are needed after closing prematurely. Requires the admin token. Fails with 409 if the poll is already open.","parameters":[{"schema":{"type":"string","description":"Admin token for the poll","example":"adm_abc123"},"required":true,"description":"Admin token for the poll","name":"token","in":"path"}],"responses":{"200":{"description":"Poll reopened successfully","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"success":{"type":"boolean","description":"Whether the poll was reopened"}},"required":["success"]},"meta":{"type":"object","properties":{"requestId":{"type":"string","description":"Unique request identifier","example":"req_abc123"}},"required":["requestId"],"description":"Response metadata"}},"required":["data","meta"],"description":"Standard success response envelope"}}}},"404":{"description":"Poll not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Machine-readable error code","example":"validation_error"},"message":{"type":"string","description":"Human-readable error message","example":"Title is required"}},"required":["code","message"],"description":"Error detail"},"meta":{"type":"object","properties":{"requestId":{"type":"string","description":"Unique request identifier","example":"req_abc123"}},"required":["requestId"],"description":"Response metadata"}},"required":["error","meta"],"description":"Error response envelope"}}}},"409":{"description":"Poll is already open","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Machine-readable error code","example":"validation_error"},"message":{"type":"string","description":"Human-readable error message","example":"Title is required"}},"required":["code","message"],"description":"Error detail"},"meta":{"type":"object","properties":{"requestId":{"type":"string","description":"Unique request identifier","example":"req_abc123"}},"required":["requestId"],"description":"Response metadata"}},"required":["error","meta"],"description":"Error response envelope"}}}}}}},"/api/v1/vote/participate/{token}":{"get":{"description":"Fetch poll data via a shared participate token. Optionally include participant data for edit mode by providing a participant token (?pt=). Returns poll details, time slots, and optionally existing participant data with votes.","parameters":[{"schema":{"type":"string","description":"Shared participate token for the poll","example":"prt_abc123"},"required":true,"description":"Shared participate token for the poll","name":"token","in":"path"},{"schema":{"type":"string","description":"Participant token for retrieving existing votes (edit mode)","example":"ptk_abc123"},"required":false,"description":"Participant token for retrieving existing votes (edit mode)","name":"pt","in":"query"}],"responses":{"200":{"description":"Poll data and time slots, optionally with existing participant data","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"poll":{"type":"object","properties":{"id":{"type":"string","description":"Poll ID","example":"poll_abc123"},"title":{"type":"string","description":"Poll title","example":"Team standup time"},"description":{"type":"string","nullable":true,"description":"Poll description","example":"Find a time for daily standup"},"status":{"type":"string","enum":["open","closed"],"description":"Current status of the poll","example":"open"},"timeSlots":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Unique time slot identifier","example":"ts_abc123"},"date":{"type":"string","description":"Date in YYYY-MM-DD format","example":"2026-02-10"},"startTime":{"type":"string","description":"Start time in HH:MM format","example":"09:00"},"endTime":{"type":"string","description":"End time in HH:MM format","example":"09:30"}},"required":["id","date","startTime","endTime"],"description":"Time slot with ID","example":{"id":"ts_abc123","date":"2026-02-10","startTime":"09:00","endTime":"09:30"}},"description":"Available time slots to vote on"}},"required":["id","title","description","status","timeSlots"],"description":"Poll data for participate-token voting"},"existingParticipant":{"type":"object","properties":{"name":{"type":"string","description":"Participant's display name","example":"Alice"},"email":{"type":"string","nullable":true,"description":"Participant's email","example":"alice@example.com"},"votes":{"type":"object","additionalProperties":{"type":"string","enum":["yes","maybe","no"],"description":"Availability vote for a time slot","example":"yes"},"description":"Existing votes keyed by time slot ID","example":{"ts_001":"yes","ts_002":"maybe"}}},"required":["name","email","votes"],"description":"Present when ?pt= query param provided and participant found"}},"required":["poll"],"description":"Vote participate response containing poll data and time slots, optionally with existing participant data"},"meta":{"type":"object","properties":{"requestId":{"type":"string","description":"Unique request identifier","example":"req_abc123"}},"required":["requestId"],"description":"Response metadata"}},"required":["data","meta"],"description":"Standard success response envelope"}}}},"404":{"description":"Participate token not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Machine-readable error code","example":"validation_error"},"message":{"type":"string","description":"Human-readable error message","example":"Title is required"}},"required":["code","message"],"description":"Error detail"},"meta":{"type":"object","properties":{"requestId":{"type":"string","description":"Unique request identifier","example":"req_abc123"}},"required":["requestId"],"description":"Response metadata"}},"required":["error","meta"],"description":"Error response envelope"}}}}}},"post":{"description":"Submit votes via a shared participate token. If a participant with the same email already exists on this poll, updates their votes. Otherwise creates a new participant. Returns a participantToken for future edits.","parameters":[{"schema":{"type":"string","description":"Shared participate token for the poll","example":"prt_abc123"},"required":true,"description":"Shared participate token for the poll","name":"token","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"description":"Display name for the participant","example":"Alice"},"email":{"type":"string","format":"email","description":"Participant email for identification and edit access","example":"alice@example.com"},"votes":{"type":"array","items":{"type":"object","properties":{"timeSlotId":{"type":"string","description":"Time slot ID","example":"ts_abc123"},"value":{"type":"string","enum":["yes","maybe","no"],"description":"Availability vote for a time slot","example":"yes"}},"required":["timeSlotId","value"]},"description":"Array of votes for time slots"}},"required":["name","email","votes"],"description":"Input for submitting votes via participate token"}}}},"responses":{"200":{"description":"Votes submitted successfully","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"participant":{"type":"object","properties":{"id":{"type":"string","description":"Participant ID","example":"part_abc123"},"name":{"type":"string","description":"Participant display name","example":"Alice"},"votedAt":{"type":"string","description":"ISO 8601 timestamp of vote submission","example":"2026-02-01T12:00:00Z"},"participantToken":{"type":"string","description":"Secret token for editing this participant's votes","example":"ptk_abc123"}},"required":["id","name","votedAt","participantToken"]},"isUpdate":{"type":"boolean","description":"Whether this submission updated an existing participant's votes","example":false}},"required":["success","participant","isUpdate"],"description":"Response after successfully submitting votes via participate token"},"meta":{"type":"object","properties":{"requestId":{"type":"string","description":"Unique request identifier","example":"req_abc123"}},"required":["requestId"],"description":"Response metadata"}},"required":["data","meta"],"description":"Standard success response envelope"}}}},"404":{"description":"Participate token not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Machine-readable error code","example":"validation_error"},"message":{"type":"string","description":"Human-readable error message","example":"Title is required"}},"required":["code","message"],"description":"Error detail"},"meta":{"type":"object","properties":{"requestId":{"type":"string","description":"Unique request identifier","example":"req_abc123"}},"required":["requestId"],"description":"Response metadata"}},"required":["error","meta"],"description":"Error response envelope"}}}},"409":{"description":"Poll is closed","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Machine-readable error code","example":"validation_error"},"message":{"type":"string","description":"Human-readable error message","example":"Title is required"}},"required":["code","message"],"description":"Error detail"},"meta":{"type":"object","properties":{"requestId":{"type":"string","description":"Unique request identifier","example":"req_abc123"}},"required":["requestId"],"description":"Response metadata"}},"required":["error","meta"],"description":"Error response envelope"}}}}}}},"/api/v1/auth/request-verification":{"post":{"description":"Request an email verification link. The link is sent to the provided email and expires after 1 hour. Rate limited per email and IP address.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","format":"email","description":"Email address to verify","example":"creator@example.com"},"context":{"type":"object","properties":{"title":{"type":"string","description":"Poll title to pre-fill after verification","example":"Team standup time"},"description":{"type":"string","description":"Poll description to pre-fill after verification","example":"Find a time for daily standup"}},"description":"Optional context preserved through verification"},"source":{"type":"string","enum":["web","api"],"default":"web","description":"Source of the verification request (web or api)","example":"web"},"turnstileToken":{"type":"string","description":"Cloudflare Turnstile CAPTCHA token (required for web requests)","example":"0.turnstile_token_here"}},"required":["email"],"description":"Input for requesting email verification"}}}},"responses":{"200":{"description":"Verification email sent","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"sent":{"type":"boolean","description":"Whether a verification email was sent","example":true},"email":{"type":"string","format":"email","description":"Normalized email address","example":"creator@example.com"},"expiresIn":{"type":"number","description":"Seconds until the verification link expires","example":3600}},"required":["sent","email","expiresIn"],"description":"Response after requesting email verification"},"meta":{"type":"object","properties":{"requestId":{"type":"string","description":"Unique request identifier","example":"req_abc123"}},"required":["requestId"],"description":"Response metadata"}},"required":["data","meta"],"description":"Standard success response envelope"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Machine-readable error code","example":"validation_error"},"message":{"type":"string","description":"Human-readable error message","example":"Title is required"}},"required":["code","message"],"description":"Error detail"},"meta":{"type":"object","properties":{"requestId":{"type":"string","description":"Unique request identifier","example":"req_abc123"}},"required":["requestId"],"description":"Response metadata"}},"required":["error","meta"],"description":"Error response envelope"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Machine-readable error code","example":"validation_error"},"message":{"type":"string","description":"Human-readable error message","example":"Title is required"}},"required":["code","message"],"description":"Error detail"},"meta":{"type":"object","properties":{"requestId":{"type":"string","description":"Unique request identifier","example":"req_abc123"}},"required":["requestId"],"description":"Response metadata"}},"required":["error","meta"],"description":"Error response envelope"}}}}}}},"/api/v1/auth/status":{"get":{"description":"Check whether an email address is verified. Returns the verification status and timestamp.","parameters":[{"schema":{"type":"string","format":"email","description":"Email address to check","example":"creator@example.com"},"required":true,"description":"Email address to check","name":"email","in":"query"}],"responses":{"200":{"description":"Verification status","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"email":{"type":"string","format":"email","description":"Normalized email address","example":"creator@example.com"},"verified":{"type":"boolean","description":"Whether the email is currently verified","example":true},"verifiedAt":{"type":"string","description":"ISO 8601 timestamp of when the email was verified","example":"2026-02-01T12:00:00Z"}},"required":["email","verified"],"description":"Email verification status"},"meta":{"type":"object","properties":{"requestId":{"type":"string","description":"Unique request identifier","example":"req_abc123"}},"required":["requestId"],"description":"Response metadata"}},"required":["data","meta"],"description":"Standard success response envelope"}}}},"400":{"description":"Validation error (missing or invalid email)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Machine-readable error code","example":"validation_error"},"message":{"type":"string","description":"Human-readable error message","example":"Title is required"}},"required":["code","message"],"description":"Error detail"},"meta":{"type":"object","properties":{"requestId":{"type":"string","description":"Unique request identifier","example":"req_abc123"}},"required":["requestId"],"description":"Response metadata"}},"required":["error","meta"],"description":"Error response envelope"}}}}}}}}}