Skip to main content

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:

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:

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.