Skip to main content

Conventions

Response envelope

Every response is JSON. Two shapes:

Success

{
"data": <object or array>,
"meta": <optional, e.g. pagination>
}

Error

{
"error": {
"code": "validation_failed",
"message": "Request validation failed",
"details": [
{ "field": "end_seconds", "message": "must be greater than start_seconds" }
]
}
}

Error codes

HTTPcodeWhen
401unauthorizedMissing or invalid API key
402plan_requiredFree plan or no plan — upgrade to use the API
402quota_exceededPlan-level credit (project, transcription, etc.) used up
403forbiddenCaller doesn't own the resource, or is a participant
404not_foundResource doesn't exist (or is in another workspace)
422validation_failedBody or query params didn't validate
429rate_limitedPer-minute request cap exceeded — see Retry-After
500internalSomething broke our side

Always branch on error.code, not on the message — messages are human-readable and may change.

Pagination

List endpoints take ?limit (default 25, max 100) and ?cursor. The response carries meta.next_cursor; pass it back to fetch the next page. null means you've reached the end.

curl "https://api.userevaluation.com/v1/projects?limit=25"
# response: { "data": [...], "meta": { "next_cursor": "eyJsYXN0SWQi..." } }

curl "https://api.userevaluation.com/v1/projects?limit=25&cursor=eyJsYXN0SWQi..."

Cursors are opaque — don't try to decode them. They embed an internal id and orderings; if we change the sort, we may invalidate cursors mid-pagination.

Idempotency

Send Idempotency-Key: <unique-string> on any POST to make the request safe to retry. We cache the first 2xx response and replay it for any subsequent request with the same key (per-user, 24h TTL).

curl -X POST https://api.userevaluation.com/v1/projects \
-H "Authorization: Bearer ue_live_..." \
-H "Idempotency-Key: my-create-project-2026-05-12-a" \
-H "Content-Type: application/json" \
-d '{"name": "New study"}'

A retry with the same key returns the same project — not a duplicate.

Use a UUID, a hash of your business object, or any string unique-per-write that you can regenerate if needed. The same key from a different user is a different cache entry.

Dates and times

All dates are ISO-8601 UTC strings: 2026-05-12T14:32:01.234Z.

The one exception is engage-test deadlines (postDetails.deadline), which are wall-clock dates in the format YYYY-MM-DD. This is intentional — researchers pick a calendar date, and we treat it as inclusive end-of-day on that date in the participant's timezone.

IDs

IDs are opaque strings. Don't depend on the format — it happens to be a Mongo ObjectId today, may not be tomorrow. Use them as-is in URLs and bodies.

Versioning

This is /v1. We promise not to:

  • rename or remove fields
  • change a field's type
  • change a status code
  • change an error code

We reserve the right to:

  • add new fields to responses
  • add new endpoints
  • relax validation (accept previously-rejected inputs)
  • accept new optional request fields

A breaking change means /v2. We'll announce it with at least 12 months of overlap.

Your client should ignore unknown response fields gracefully.