REST API (v1)
A small, read-only API for pulling your own assessment data out of Scorafy. It is available on the Pro plan and above.
What this API does not do
- No writes. There are no endpoints to create, update or delete anything. Keys are read-only.
- No access to reports before release. A report is retrievable only once an assessor has released it.
- No access to another organisation's data. The organisation is taken from the key, never from the request.
Authentication
Create a key in Settings - API keys. It is shown once, at creation. Send it as a bearer token on every request:
curl https://scorafy.com/api/v1/assessments \
-H "Authorization: Bearer sk_live_..."Keys can be revoked at any time in Settings. A revoked or expired key returns 401. A key belonging to an organisation without API access on its current plan returns 403.
Australian data-residency customers call https://au.scorafy.com instead. Keys are not shared between the two environments.
Rate limit
60 requests per minute per key, across all endpoints. Exceeding it returns 429 with a Retry-After header.
Errors
Every non-2xx response uses the same envelope:
{ "error": { "code": "not_found", "message": "Assessment not found." } }Codes: unauthorised (401), forbidden (403), not_found (404), invalid_request (400), rate_limited (429), server_error (500). An id that does not exist and an id belonging to another organisation both return 404.
Endpoints
GET /api/v1/assessments
Lists your assessments, newest first. Query parameters: limit (1-100, default 25), offset (default 0), status (draft, live or archived).
{
"data": [
{
"id": "8f2c...",
"title": "Sales Capability Review",
"slug": "sales-capability-review",
"status": "live",
"response_count": 42,
"created_at": "2026-07-02T09:14:00.000Z",
"updated_at": "2026-08-01T11:02:00.000Z",
"published_at": "2026-07-03T08:00:00.000Z",
"archived_at": null
}
],
"pagination": { "limit": 25, "offset": 0, "total": 7, "has_more": false }
}GET /api/v1/assessments/:id/responses
Lists the responses to one assessment. Same limit and offset parameters, plus status (in_progress or completed). Unreleased responses are included - the released flag tells you whether the report can be fetched yet.
{
"data": [
{
"id": "1a4e...",
"assessment_id": "8f2c...",
"status": "completed",
"respondent_name": "Jordan Lee",
"respondent_email": "jordan@example.com",
"pii_anonymised": false,
"is_test": false,
"total_score": 34,
"max_possible_score": 40,
"score_percentage": 85,
"manual_score": null,
"manual_score_max": null,
"released": true,
"released_at": "2026-08-04T10:22:00.000Z",
"started_at": "2026-08-03T14:05:00.000Z",
"completed_at": "2026-08-03T14:41:00.000Z"
}
],
"pagination": { "limit": 25, "offset": 0, "total": 42, "has_more": true }
}If your assessment has a PII retention period set, respondent name and email are removed once it elapses. pii_anonymised is then true and those fields are null. That is the retention policy working, not missing data.
GET /api/v1/responses/:id/report
Returns the AI report for one response. Returns 404 while the report is still generating, and 404 with an explanatory message until an assessor releases it.
{
"data": {
"response_id": "1a4e...",
"assessment_id": "8f2c...",
"assessment_title": "Sales Capability Review",
"status": "completed",
"total_score": 34,
"max_possible_score": 40,
"score_percentage": 85,
"report_content": "...",
"report_sections": { "...": "..." },
"completed_at": "2026-08-03T14:41:00.000Z",
"released_at": "2026-08-04T10:22:00.000Z",
"generated_at": "2026-08-03T14:42:10.000Z"
}
}Webhooks
Webhooks are configured separately in Settings - Webhooks and are available on the same plans as API keys. Events currently delivered:
response.completed- a respondent has submitted.assessment.published- an assessment went live.assessment.archived- an assessment was archived.
Each request carries an X-Scorafy-Event header and an X-Scorafy-Signature header: the HMAC-SHA256 of the raw request body, keyed with your webhook secret, as lowercase hex. Compare it in constant time before trusting the payload.
Delivery guarantees - read this before you rely on them
Delivery is at-least-once, with day-scale recovery. Scorafy attempts the first delivery at the moment of the event. If your endpoint does not return a 2xx, the delivery is queued and retried by a worker that runs once per day. In practice:
- attempt 1: immediately, when the event happens;
- attempt 2: on the next daily pass - up to about 24 hours later;
- attempt 3: about a day after that;
- attempts 4 and 5: about two and four days after that;
- after five failed attempts the delivery is given up on and recorded as failed.
So an endpoint that is down for two minutes will still receive its event - but roughly a day late, not a minute late. If you need near-real-time recovery, treat the API as the source of truth and poll it, rather than depending on webhook retries. A destination that fails 20 times in a row is switched off automatically and shown as inactive in Settings.
Retries reuse the original payload and are re-signed with your webhook's current secret, so rotating a secret does not strand queued deliveries. Payloads are not deduplicated - use the event data to make your handler idempotent.
Versioning
The path carries the version. Fields may be added to responses without notice; existing fields will not be removed or repurposed within v1. Write endpoints, if they are ever added, will require a new key scope rather than silently widening existing keys.