Public API
Aspire & Thrive — public API
Read-only paginated access to young person records.
Auth
An administrator can make an API key with the read:cyps scope from /settings/api-keys. The key chooses its organisation; a caller cannot select another one with a header. Send three headers on each record-list request:
Authorization: Bearer <prefix>.<secret>
X-Aspire-Timestamp: <unix seconds>
X-Aspire-Signature: <hmac_sha256(${timestamp}.${method}.${path}.${sha256(body)}, secret)>Sign the uppercase method, the exact pathname and query string (including the ?), and the SHA-256 hash of the body. For GET the body is empty. Use the secret after the key's first dot as the HMAC key, with a Unix-seconds timestamp within five minutes of the server's clock. The OpenAPI JSON can be read without a key.
Endpoints
| Method | Path | Summary |
|---|---|---|
| GET | /api/v1/cyps | List young people for the API key's organisation |
The record list accepts limit (1–100, default 50), status and cursor. Pass the returned next_cursor into the next request until it is empty. This endpoint allows 60 requests per minute per API key; wait for Retry-After after a rate-limit response. Single-record lookup and webhook subscription through API keys are not currently available.
Signed request with Node.js
Save the example as list-records.mjs and run it with Node.js 18 or later. Set AT_API_KEY in your environment and optionally AT_API_ORIGIN to your application address. Keep the key out of source code and logs.
import { createHash, createHmac } from "node:crypto";
const key = process.env.AT_API_KEY;
const dot = key?.indexOf(".") ?? -1;
if (!key || dot < 1 || dot === key.length - 1) throw new Error("Set AT_API_KEY to the full prefix.secret key.");
const url = new URL("/api/v1/cyps?limit=10", process.env.AT_API_ORIGIN || "https://aspire-and-thrive-web-9wzj.vercel.app");
const timestamp = String(Math.floor(Date.now() / 1000));
const bodyHash = createHash("sha256").update("").digest("hex");
const message = [timestamp, "GET", url.pathname + url.search, bodyHash].join(".");
const signature = createHmac("sha256", key.slice(dot + 1)).update(message).digest("hex");
const response = await fetch(url, {
method: "GET",
headers: {
Authorization: "Bearer " + key,
"X-Aspire-Timestamp": timestamp,
"X-Aspire-Signature": signature,
},
});
console.log("Status:", response.status);
if (response.ok) {
const result = await response.json();
console.log("Returned records:", result.data.length);
console.log("More pages:", result.next_cursor !== null);
} else {
console.log("Request failed. Check the status against the API documentation.");
}Outbound webhooks
Operators subscribe to events from /admin/webhooks. Each delivery POSTs the envelope below to the subscriber URL with three headers: X-AT-Signature: sha256=<hex>, X-AT-Event, and X-AT-Delivery. Verify the signature using the per-webhook signing secret.
concern.createdsession.submittedhtr.signed-off
Zapier / n8n
There is no published Zapier app yet. Both tools work today using their generic HTTP blocks:
- Zapier as a trigger — add a Webhooks by Zapier · Catch Hook step, then subscribe its URL from
/admin/webhooksto the events you want. VerifyX-AT-Signaturein a Code by Zapier step. - Zapier as an action — mint a key from
/settings/api-keysand call the API with Webhooks by Zapier · Custom Request, generating the three headers above in a code step. - n8n — the Webhook node receives Daymello events the same way; the HTTP Request node can call the API with a Function node signing the request.