Skip to main content
/v1 replaces the unversioned private API at https://app.rallly.co/api/private. The private API is frozen: it still answers, but it receives no new fields or endpoints, and everything new lands on /v1 only. Your API keys, poll IDs, invite links and rate limits carry over unchanged. What changes is the base URL, the request and response shapes, and how errors look.

Summary of changes

Global changes

Base URL

Replace the host and prefix. The poll paths under it are the same, with two exceptions: PATCH /polls/{pollId} is removed, and /docs has no v1 equivalent. Both are covered below.

Error envelope

Every v1 failure is JSON with the same shape, including cases the private API answered in plain text or with the validator’s raw output. Branch on error.code; error.message is for humans.
UNAUTHORIZED, INVALID_AUTHORIZATION_HEADER, SPACE_NOT_PRO, ORGANIZER_NOT_MEMBER, TOO_MANY_OPTIONS, INAPPROPRIATE_CONTENT, POLL_NOT_FOUND, RATE_LIMIT_EXCEEDED and SERVICE_UNAVAILABLE keep their status codes and meanings. See Errors for the full list.

Strict request bodies

The private API silently dropped fields it did not know, including spaceId, which it accepted but never used. v1 rejects any field outside the documented schema with VALIDATION_ERROR. Remove spaceId and any other extra fields before switching.

The poll resource

Every endpoint that returns a poll (POST /polls, GET /polls, GET /polls/{pollId}) returns the same v1 shape. Compared with the private API:
  • timezone is renamed timeZone.
  • user is replaced by organizer, which adds id and email.
  • kind is added: date or time. It tells you which option shape to expect.
  • updatedAt is added. Compare it between reads to notice changes.
  • The five poll settings are returned as booleans.
  • participantCount is on every poll, not only list items.
  • Options in a date poll carry date instead of startTime and duration: 0. Options in a time poll are unchanged.

Endpoints

Create a poll

POST /api/private/pollsPOST /v1/polls Status code. v1 responds 201 Created instead of 200 OK. A client that checks for exactly 200 will treat every successful create as a failure. Request. The private API chose the poll type from whether you sent dates or slots. v1 takes an explicit kind and puts every option under options. Each request option is the response option without its id: { date } for a date poll, { startTime, duration? } for a time poll. The body is strict, so the other response fields (id, status, createdAt, participantCount and so on) must not be sent. Date polls:
Time polls: the slots wrapper is gone. duration and timeZone move to the top level, explicit times become options objects, and slot generators move to their own generators array.
Field by field: Everything else (description, location, requireEmail, hideParticipants, hideScores, disableComments, allowTentativeVotes, organizer) is unchanged. The interpretation of a startTime without an offset is also unchanged: wall clock time in timeZone when set, floating otherwise. Generator validation. The private API expanded a generator and only then failed with NO_OPTIONS_GENERATED if nothing came out. v1 rejects an impossible generator up front with VALIDATION_ERROR naming the field: to not later than from, a daily window shorter than duration, or a date range containing none of the listed days. Times in a generator are HH:mm; the private API also accepted seconds. Response. The full poll, in the v1 shape.

List polls

GET /api/private/pollsGET /v1/polls Request. Unchanged. status, cursor and limit work the same way, and nextCursor is still returned beside data. Response. Each item is the full v1 poll. The private API’s list items already carried participantCount; the rest of the new fields are additions, and the renames (timezone, user, date options) apply here too.

Get a poll

GET /api/private/polls/{pollId}GET /v1/polls/{pollId} Request. Unchanged. Response. The v1 poll. Note that participantCount is now included, so a client that called both GET /polls/{pollId} and GET /polls/{pollId}/results just to count participants can drop the second call.

Update a poll

PATCH /api/private/polls/{pollId}removed
There is no v1 endpoint that changes a poll’s status. Its only supported use was closing a poll with { "status": "closed" }.
Polls close on their own once every option is in the past. To close a poll earlier, or to reopen one, use the app. If your integration polled results and stopped once the poll was closed, keep reading status from GET /polls/{pollId}/results; that part is unchanged.

Get poll results

GET /api/private/polls/{pollId}/resultsGET /v1/polls/{pollId}/results Request. Unchanged. Response. Three changes:
  • kind is added at the top level.
  • Options in a date poll carry date instead of startTime and duration: 0, matching the poll resource.
  • votes is dense. The private API omitted any vote type with zero votes, so an option nobody voted on had votes: []. v1 lists every vote type the poll offers, in display order, with count: 0 where nobody chose it. A client that summed votes or looked a type up by type keeps working; a client that treated an empty array as “no votes” must check the counts instead.
score, isTopChoice and highScore are computed the same way today, but v1 documents score as opaque: sort by it and use isTopChoice or highScore to find the leaders, but do not decode it into vote counts or compare it across polls. Vote types are an open set in v1, so tolerate a type you do not recognise.

Get poll participants

GET /api/private/polls/{pollId}/participantsGET /v1/polls/{pollId}/participants Request. Unchanged. Response. data is the array itself. The private API wrapped it in an object with pollId; v1 drops the wrapper, since you already know the poll ID from the URL. The participant fields (id, name, email, createdAt) and the ordering (oldest response first) are unchanged.

Delete a poll

DELETE /api/private/polls/{pollId}DELETE /v1/polls/{pollId} Unchanged. Responds 200 with { "data": { "id", "deleted": true } }, or 404 POLL_NOT_FOUND.

OpenAPI document and docs page

GET /api/private/openapiGET /v1/openapi The spec moves with the API. The Scalar page at /api/private/docs has no v1 equivalent; this site is the reference. Both spec endpoints are public and need no key.

Checklist

  1. Change the base URL to https://api.rallly.co/v1.
  2. Accept 201 from POST /polls.
  3. Rewrite create requests: add kind, replace dates and slots with options and generators, rename timezone to timeZone and the generator’s startTime and endTime to from and to, and remove spaceId and any other undocumented fields.
  4. Read timeZone instead of timezone, and organizer instead of user, in every poll response.
  5. Branch on kind when reading options: date for date polls, startTime and duration for time polls.
  6. In results, stop treating an empty votes array as “no votes”; read the counts.
  7. Read participants from data directly.
  8. Parse every error as { "error": { "code", "message" } } and drop any handling of the private API’s validator output, DUPLICATE_DATES, NO_OPTIONS_GENERATED and TRANSITION_NOT_AVAILABLE.
  9. Remove any call to PATCH /polls/{pollId}.