Integration API
HMAC-signed partner API for creating, updating, reading and syncing YDONE tasks from external systems.
Base URL
https://ydone.appPreview environment: https://project--6c597736-5b05-4ad7-8bd1-20789c8ec13a-dev.lovable.app
Authentication
Every request except /health must include x-ydone-tenant, x-ydone-timestamp and x-ydone-signature an HMAC-SHA256 signature computed over `${timestamp}.${rawBody}` using your tenant secret. Set the secret via PARTNER_API_SECRET.
Browser requests are rejected
Requests carrying an Origin header or Sec-Fetch-* hints are treated as browser traffic and rejected with 403 browser_client_forbidden. Call the API from your server only.
import crypto from "node:crypto";
const BASE_URL = "https://ydone.app";
// Each tenant has its own HMAC secret. Rotate it in YDONE Platform admin → Partner portals.
const SECRET = process.env.YDONE_TENANT_SECRET;
const TENANT = "jsc-solutions";
async function call(path, method = "GET", payload) {
const rawBody = payload ? JSON.stringify(payload) : "";
const timestamp = Math.floor(Date.now() / 1000).toString();
const signature = crypto
.createHmac("sha256", SECRET)
.update(`${timestamp}.${rawBody}`)
.digest("hex");
const res = await fetch(BASE_URL + path, {
method,
headers: {
"content-type": "application/json",
"x-ydone-tenant": TENANT,
"x-ydone-timestamp": timestamp,
"x-ydone-signature": signature,
},
...(rawBody ? { body: rawBody } : {}),
});
return res.json();
}Endpoints
| GET | /api/public/v1/health | Liveness probe — no signature required |
| POST | /api/public/v1/tasks/upsert | Batch upsert (max 200) keyed on (external_system, external_id) |
| GET | /api/public/v1/tasks?since=&limit=&cursor= | Delta sync incl. soft-deleted tasks (deleted: true) |
| GET | /api/public/v1/tasks/:id | Single task |
| POST | /api/public/v1/tasks/:id/status | Lightweight status change with optional comment |
| DELETE | /api/public/v1/tasks/:id | Soft delete |
| POST | /api/public/v1/activation-links | Create a one-time YDONE activation link for an organization admin |
| GET | /api/public/v1/activation-links | List activation links of your tenant (no tokens) |
| POST | /api/public/v1/activation-links/:id/revoke | Revoke an unused activation link |
| POST | /api/public/v1/workspaces/upsert | Mirror partner areas (phase 2) |
| GET | /api/public/v1/workspaces | List mirrored areas (phase 2) |
Upsert tasks
POST /api/public/v1/tasks/upsert
{
"tenant": "jsc-solutions",
"tasks": [
{
"external_id": "0f8f2c9e-2f1a-4f21-9a0e-2b6a1d6f9a11",
"source_module": "ems_gap_item",
"source_ref_id": "GAP-2024-118",
"source_ref_label": "Gap 118 — Missing calibration record",
"source_url": "https://jsc.example.com/ems/gaps/118",
"title": "Recalibrate torque wrenches in line 3",
"description": "Calibration overdue since audit finding 118.",
"status": "in_progress",
"priority": "high",
"category": "Quality",
"assignee_name": "Mara Kessler",
"assignee_email": "mara.kessler@example.com",
"start_date": "2026-08-20",
"due_date": "2026-09-15",
"progress": 40,
"tags": ["audit", "calibration"],
"root_cause": "No recurring calibration schedule",
"effectiveness_check": "Audit re-check in 90 days",
"effectiveness_result": null,
"completed_at": null
}
]
}
200 OK
{
"results": [
{ "external_id": "0f8f…9a11", "id": "6a1c…", "action": "created", "updated_at": "2026-08-23T21:40:11.204Z" }
]
}action is createdwhen a new task is inserted, updated when an existing one is modified, and unchanged. Tasks are matched on (external_system, external_id).
Delta sync
GET /api/public/v1/tasks?since=2026-08-01T00:00:00Z&limit=100
200 OK
{
"tasks": [ { "id": "…", "external_id": "…", "status": "done", "deleted": false, "updated_at": "…" } ],
"next_cursor": "2026-08-23T21:40:11.204Z",
"has_more": false,
"server_time": "2026-08-23T21:41:00.000Z"
}Activation links
Create a one-time YDONE activation link so an organization admin can set up their account without a manual invite.
POST /api/public/v1/activation-links
{
"tenant": "jsc-solutions",
"email": "admin@kunde.de",
"role": "admin",
"display_name": "Mara Kessler",
"external_user_id": "portal-user-4711",
"expires_in_days": 14
}
201 Created
{
"activation_link": {
"id": "…",
"email": "admin@kunde.de",
"role": "admin",
"expires_at": "2026-09-07T09:15:00.000Z",
"url": "https://ydone.app/activate?token=8Jd…",
"token": "8Jd…"
}
}
# revoke an unused link
POST /api/public/v1/activation-links/<id>/revoke
# list links (optional ?email=)
GET /api/public/v1/activation-linksWebhooks
Subscribe to task.created, task.updatedand task.status_changed, task.deletedevents to keep your system in sync in real time.
POST <your target_url>
x-ydone-event: task.status_changed
x-ydone-timestamp: 1789251611
x-ydone-signature: <hmac-sha256 of "timestamp.rawBody" with the webhook secret>
{ "event": "task.status_changed", "task": { "id": "…", "external_id": "…", "status": "done" } }Error codes
| 401 | missing_auth_headers | tenant, timestamp or signature header missing |
| 401 | invalid_timestamp | timestamp is not unix seconds |
| 401 | timestamp_expired | timestamp older/newer than 5 minutes |
| 401 | invalid_signature | HMAC mismatch |
| 403 | tenant_not_allowed | unknown or deactivated tenant |
| 403 | tenant_mismatch | body tenant ≠ signed header tenant |
| 400 | invalid_json / invalid_id / invalid_since | malformed request |
| 422 | validation_failed | payload violates schema (see issues[]) |
| 404 | task_not_found | task does not exist for this tenant |
| 404 | not_found | activation link unknown, already redeemed or not yours |
| 500 | query_failed / update_failed / delete_failed | server-side error |
| 503 | api_not_configured | signing secret not configured |