openapi: 3.0.3
info:
title: 'Linked Wellbeing API Documentation'
description: 'API documentation for Linked Wellness, including the V2 embedded-delivery SSO endpoints used by partner integrations.'
version: 1.0.0
servers:
-
url: 'https://api.linkedwellness.ie'
tags:
-
name: 'V2 - Partner SSO'
description: "\nServer-to-server handoff that signs a member of your platform into their Linked\nWellness account without a password or registration form.\n\n**Sequence:**\n\n1. A member opens the embedded wellbeing product inside your platform.\n2. Your **backend** mints a short-lived HS256 JWT assertion and POSTs it to the\n handoff endpoint below. Never do this from the browser — the shared secret\n must not leave your servers.\n3. We verify the assertion, create or match the member's account, and return a\n single-use `consume_url` (valid for 60 seconds).\n4. Your platform redirects the member's **browser** (top-level or iframe) to that\n `consume_url`. That request sets the session cookie and lands them on their\n dashboard — no further action from your side.\n\nAll error responses include a stable machine-readable `error` code (see the\nError catalog in the introduction) alongside a human-readable `message`."
-
name: 'V1 - Content'
description: "\nRead-only content API. All endpoints require an API key, sent as an\n`X-API-Key` header (preferred) or an `api_key` query parameter."
-
name: V1
description: ''
components:
securitySchemes:
default:
type: apiKey
name: X-API-Key
in: header
description: 'API keys are issued during integration onboarding — contact us to receive yours. Send the key with every request, either as an X-API-Key header (preferred) or an api_key query parameter.'
security:
-
default: []
paths:
'/api/v2/partners/{partner}/sso':
post:
summary: 'Verify a member assertion and issue a one-time consume URL.'
operationId: verifyAMemberAssertionAndIssueAOneTimeConsumeURL
description: "Rate limited to 30 requests per minute per IP; expect `429 Too Many Requests`\nwith a `Retry-After` header beyond that."
parameters: []
responses:
200:
description: 'Assertion accepted'
content:
application/json:
schema:
type: object
example:
consume_url: 'https://example.linkedwellness.ie/partner-sso/consume?token=Xy82jbqT...'
properties:
consume_url:
type: string
example: 'https://example.linkedwellness.ie/partner-sso/consume?token=Xy82jbqT...'
401:
description: ''
content:
application/json:
schema:
oneOf:
-
description: 'Bad signature or malformed token'
type: object
example:
error: invalid_assertion
message: 'The assertion signature is invalid or the token is malformed. Check the shared secret and HS256 signing.'
properties:
error:
type: string
example: invalid_assertion
message:
type: string
example: 'The assertion signature is invalid or the token is malformed. Check the shared secret and HS256 signing.'
-
description: 'Assertion too old (iat/exp)'
type: object
example:
error: assertion_expired
message: 'The assertion iat is missing or older than 90 seconds. Mint assertions immediately before posting them.'
properties:
error:
type: string
example: assertion_expired
message:
type: string
example: 'The assertion iat is missing or older than 90 seconds. Mint assertions immediately before posting them.'
-
description: 'jti reused'
type: object
example:
error: assertion_replayed
message: 'This assertion jti has already been used. Mint a new assertion with a fresh jti for every handoff.'
properties:
error:
type: string
example: assertion_replayed
message:
type: string
example: 'This assertion jti has already been used. Mint a new assertion with a fresh jti for every handoff.'
403:
description: 'Integration disabled'
content:
application/json:
schema:
type: object
example:
error: integration_disabled
message: 'This partner integration is currently disabled. Contact support.'
properties:
error:
type: string
example: integration_disabled
message:
type: string
example: 'This partner integration is currently disabled. Contact support.'
404:
description: 'Unknown partner slug'
content:
application/json:
schema:
type: object
example:
error: unknown_partner
message: 'Unknown partner "acme".'
properties:
error:
type: string
example: unknown_partner
message:
type: string
example: 'Unknown partner "acme".'
422:
description: 'Missing assertion field'
content:
application/json:
schema:
type: object
example:
message: 'The assertion field is required.'
errors:
assertion:
- 'The assertion field is required.'
properties:
message:
type: string
example: 'The assertion field is required.'
errors:
type: object
properties:
assertion:
type: array
example:
- 'The assertion field is required.'
items:
type: string
429:
description: 'Rate limited'
content:
application/json:
schema:
type: object
example:
message: 'Too Many Attempts.'
properties:
message:
type: string
example: 'Too Many Attempts.'
tags:
- 'V2 - Partner SSO'
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
assertion:
type: string
description: 'Signed HS256 JWT (see "JWT assertion requirements" in the introduction). Required claims: sub, iat, exp, jti. Optional: email, name.'
example: eyJhbGciOiJIUzI1NiJ9...
required:
- assertion
security: []
parameters:
-
in: path
name: partner
description: 'Partner slug issued to you during onboarding.'
example: acme
required: true
schema:
type: string
/partner-sso/consume:
get:
summary: 'Consume a one-time sign-in link (browser navigation).'
operationId: consumeAOneTimeSignInLinkbrowserNavigation
description: "This is the URL returned by the handoff endpoint. It is **not an API call**:\nredirect the member's browser (top-level or iframe) to it as-is. It always\nresponds with a `302` redirect and never returns JSON.\n\nBehavior:\n- **Valid token** — the member's session is established (cookie set for the\n tenant domain) and the browser is redirected to `/dashboard`. On their very\n first visit they may be asked to pick a life stage before the dashboard.\n- **Expired, reused, or unknown token** — redirected to the login page with a\n friendly \"sign-in link has expired\" notice. Tokens are single-use and expire\n 60 seconds after issue, so redirect promptly and never cache or reuse a\n consume URL; recover by requesting a fresh handoff."
parameters:
-
in: query
name: token
description: 'The one-time token from `consume_url`, exactly as returned.'
example: Xy82jbqT...
required: true
schema:
type: string
description: 'The one-time token from `consume_url`, exactly as returned.'
example: Xy82jbqT...
responses:
302:
description: ''
content:
application/json:
schema:
oneOf:
-
description: 'Valid token: session established, redirect to /dashboard'
type: object
example: { }
properties: { }
-
description: 'Expired/reused token: redirect to /login with an explanatory notice'
type: object
example: { }
properties: { }
503:
description: ''
content:
text/plain:
schema:
type: string
example: "\n\n
Linked Wellness is currently carrying out a short update. We’ll check whether the site is back automatically; you can also refresh the page anytime.
\n\n Approximate time back:\n —\n (your local time)\n
\nThis page will reload automatically when the timer ends.
\nLinked Wellness is currently carrying out a short update. We’ll check whether the site is back automatically; you can also refresh the page anytime.
\n\n Approximate time back:\n —\n (your local time)\n
\nThis page will reload automatically when the timer ends.
\nLinked Wellness is currently carrying out a short update. We’ll check whether the site is back automatically; you can also refresh the page anytime.
\n\n Approximate time back:\n —\n (your local time)\n
\nThis page will reload automatically when the timer ends.
\nLinked Wellness is currently carrying out a short update. We’ll check whether the site is back automatically; you can also refresh the page anytime.
\n\n Approximate time back:\n —\n (your local time)\n
\nThis page will reload automatically when the timer ends.
\nLinked Wellness is currently carrying out a short update. We’ll check whether the site is back automatically; you can also refresh the page anytime.
\n\n Approximate time back:\n —\n (your local time)\n
\nThis page will reload automatically when the timer ends.
\nLinked Wellness is currently carrying out a short update. We’ll check whether the site is back automatically; you can also refresh the page anytime.
\n\n Approximate time back:\n —\n (your local time)\n
\nThis page will reload automatically when the timer ends.
\n