Describe the rest of the integration surface in the contract

Thirty-seven more paths the clients already call and the published spec did not
describe: the six OAuth providers' credential, status, disconnect, sync and
probe endpoints, the cross-provider surfaces under `/api/integrations/*`, the
managed record's configuration tree, and the native device-revoke path.

Where providers share a verb name they do not always share a contract, and the
descriptions say so instead of averaging it out. Three divergences are stated
per path. A disconnect refuses a repeat with 404 on six providers and answers an
idempotent 200 on two. `/api/<provider>/test` answers a bare probe result while
the same-named `/api/integrations/<provider>/test` adds the connection's last
sync, and the Google Health one accepts a body that replaces the response shape
outright. `GET /api/withings/status` refreshes and re-persists the OAuth token
while it reads; the other six status endpoints write nothing, and each now says
which it is.

Two further facts a client cannot infer. The Withings credential DELETE writes
no audit row and leaves the ledger at its last state, unlike every sibling, so
its `state` is not proof of a live connection afterwards. The WHOOP connect
ticket is a credential in a response body, returned once and stored only as a
hash.

The Google Health credential schema moves to the validations module; the managed
record's six strict patch schemas are exported from where they already lived, so
the contract publishes the objects the handler parses rather than a restatement.
This commit is contained in:
Marc-André Bombeck committed 2026-08-22 12:44:36 +02:00
1 parent c8035fdd78
commit 7f67c24ecc
8 files changed
+2110 -18

No files matched your search

+1 -12
View File
@@ -6,21 +6,10 @@ import { apiSuccess, apiError, safeJson } from "@/lib/api-response";
import { annotate } from "@/lib/logging/context";
import { encrypt } from "@/lib/crypto";
import { markDisconnected } from "@/lib/integrations/status";
import { googleHealthCredentialsSchema } from "@/lib/validations/google-health";
import { NextRequest } from "next/server";
import { z } from "zod/v4";
/**
* Per-user Google Health BYO-key credentials. Each self-hoster registers their
* own Google Cloud OAuth client (the Restricted-scope brand verification + CASA
* assessment is per-OAuth-client, so a single shared app is unworkable for a
* multi-operator product) and pastes the client id/secret into Settings. Stored
* encrypted on `User`.
*/
const googleHealthCredentialsSchema = z.object({
clientId: z.string().min(1).max(200),
clientSecret: z.string().min(1).max(200),
});
/**
* Check whether the user has Google Health BYO-key credentials stored
* (v1.26.0).