Integration API

HMAC-signed partner API for creating, updating, reading and syncing YDONE tasks from external systems.

Base URL

https://ydone.app

Preview 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/healthLiveness probe — no signature required
POST/api/public/v1/tasks/upsertBatch 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/:idSingle task
POST/api/public/v1/tasks/:id/statusLightweight status change with optional comment
DELETE/api/public/v1/tasks/:idSoft delete
POST/api/public/v1/activation-linksCreate a one-time YDONE activation link for an organization admin
GET/api/public/v1/activation-linksList activation links of your tenant (no tokens)
POST/api/public/v1/activation-links/:id/revokeRevoke an unused activation link
POST/api/public/v1/workspaces/upsertMirror partner areas (phase 2)
GET/api/public/v1/workspacesList 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-links

Webhooks

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

401missing_auth_headerstenant, timestamp or signature header missing
401invalid_timestamptimestamp is not unix seconds
401timestamp_expiredtimestamp older/newer than 5 minutes
401invalid_signatureHMAC mismatch
403tenant_not_allowedunknown or deactivated tenant
403tenant_mismatchbody tenant ≠ signed header tenant
400invalid_json / invalid_id / invalid_sincemalformed request
422validation_failedpayload violates schema (see issues[])
404task_not_foundtask does not exist for this tenant
404not_foundactivation link unknown, already redeemed or not yours
500query_failed / update_failed / delete_failedserver-side error
503api_not_configuredsigning secret not configured