{"openapi":"3.1.0","info":{"title":"instnt studio API","version":"1.0","description":"## Basics\n\nEverything lives under `https://instnt.studio/api/`. Requests and responses are UTF-8 JSON unless stated otherwise below (CSV, PDF, JavaScript). The app uses the same API; what the app can do, the API can do, within the user's permissions.\n\nEvery route also works with `.php` appended (`/api/events.php`). That keeps addresses from the old PHP API working; use the short form yourself.\n\n## Authenticating with an API key\n\nCreate a key under **Settings → API keys** and send it as `Authorization: Bearer studio_…`. Studio shows the key once and stores only a hash. A key carries the permissions of whoever created it, plus a scope: **read** (GET only), **read and write** (POST too) or **MCP** (`/api/mcp` only). The number of keys depends on the plan.\n\nA few routes are for people and never accept a key: tokens, two-factor, changing the password, deleting the workspace, viewing as a client, logging out and instnt's admin. They return `403 token_not_allowed`. Writing with a read key returns `403 token_read_only`. `GET /api/me` only looks at the session and returns `user: null` for a key.\n\n## Workspace and permissions\n\nA user belongs to exactly one workspace, so a key always works in that workspace; there is no header to choose another. The team has three roles: **owner** and **member** can manage, **viewer** can only read. A **client** only sees the projects they were given, per module (Overview, Website, Search, Product, Advice, Ads) with a level. When a client asks for a project or module that isn't theirs, Studio answers 404 rather than 403: a client doesn't need to know it exists.\n\nAn owner viewing as a client cannot change anything (`403 viewing_as_readonly`).\n\n## Reading and writing\n\nMost topics have one address: `GET` to read, with parameters in the query string (often `project_id`), and `POST` with a JSON body to write. The body has an `action` field saying what to do, for example `{\"action\": \"status\", \"id\": 12, \"status\": \"done\"}`. Unknown actions return `422 unknown_action`.\n\nLong-running work (crawl, report, refreshing advice, cookie scan) runs in the background: the answer is `202` with a `task_id`, and `GET /api/task?id=…` returns `queued`, `running`, `done` or `failed`.\n\nLists usually have a fixed cap (for example 500 recommendations or 300 pages). Paging only exists where noted below, via `page`: contacts (50 per page), the automation log (25) and the contacts in a funnel.\n\n## Responses and errors\n\nA successful response has `\"ok\": true` with the data next to it. An error has `\"ok\": false`, a fixed code in `error`, sometimes a readable explanation in `detail` (Dutch) and, for forms, a message per field in `fields`.\n\nCommon codes: `401 unauthorized` (missing or invalid key), `402 plan_limit` or `plan_feature` (the plan doesn't allow it), `403 forbidden`, `404 not_found`, `410` (expired link or invitation), `422 validation` or `unknown_action`, `429 too_many_requests` and `502` when an external service (Google, GitHub) fails.\n\n## Rate limits\n\nPer API key 120 requests per minute. Public addresses have their own limits: login 5 per minute per email address and 20 per IP, share links and status pages 30 per minute per IP, server-to-server events 600 per minute per IP, site script measurements 60 per minute per visitor, incoming automation webhooks 30 per minute per address, site events 120 per minute per address.\n\n## Other credentials\n\nBesides the personal API key there are single-purpose credentials: a **deploy key** (`POST /api/deploys` and `/api/components`), a **conversion key** per source (`POST /api/events`), a **write-back key** for the WordPress or Drupal module (`/api/titles`) and addresses with the secret in them (incoming webhooks, backup pings, data feeds). They are described with the routes.\n\nThis reference is also available as OpenAPI 3.1: `GET /api/openapi.json` (Dutch) or `?lang=en`.","contact":{"name":"instnt","email":"info@instnt.nl"}},"servers":[{"url":"https://instnt.studio"}],"tags":[{"name":"Account and sign-in","description":"Sign-in, invitations, passwords, two-factor and personal keys. Most of these are for people, not scripts."},{"name":"Workspace, team and billing","description":"Who has access, invitations, branding, invoices and instnt's admin."},{"name":"Projects and overview","description":"Projects, the cross-client overviews, dashboards, goals, the timeline and hours."},{"name":"Sources and connections","description":"Google connections, Search Console, own systems (instnt-metrics), extra services and inbound forms. Secrets never go back to the browser; a stored secret shows as `<field>_configured: true`."},{"name":"Metrics","description":"Traffic, conversion, revenue and the site script's own measurements. GA4 and Search Console come live from Google (cached); the rest from Studio's own fact tables."},{"name":"Search and SEO","description":"The query worksheet, tracked keywords, competition, the site audit, log files and content."},{"name":"Advice, questions and AI","description":"Recommendations and their status, playbooks, experiments, client questions and AI tasks."},{"name":"Uptime and technical","description":"Uptime checks, JavaScript errors, backups, maintenance, site checks, versions with known vulnerabilities and the cookie scan."},{"name":"Reports","description":"Monthly reports, quarterly and yearly overviews, share links and PDF. Clients only see published reports."},{"name":"Google Ads","description":"Insights, targets, keyword ideas and the Ads pilot with proposals."},{"name":"Automations and alerts","description":"Automations (trigger, filters, actions), the log, approvals, alert rules and the channels: outgoing webhooks, Slack and Teams. Outgoing webhooks have the shape `{\"event\", \"sent_at\", \"data\"}` with the header `X-Studio-Signature: sha256=<HMAC-SHA256 of the body with the secret>`, plus `X-Studio-Timestamp`, `X-Studio-Delivery` and `X-Studio-Signature-V2` (HMAC of `<timestamp>.<delivery>.<body>`) to reject old or replayed messages."},{"name":"Studio Mail","description":"Contacts, lists, opt-in forms, templates, sending domains and accounts (`/api/contacts`), and funnels, campaigns and segments (`/api/mail`). An unknown action returns `422 validation` here."},{"name":"Server to server","description":"Addresses for other systems: conversion events, deploys, lock files, MCP, incoming webhooks and the site script. Each has its own credential or a secret address."},{"name":"Public","description":"Without sign-in: shared reports, status pages, data feeds and Studio's own figures on the site."},{"name":"Internal","description":"Addresses for services Studio talks to. They verify the sender's signature and aren't meant to be called yourself.","x-internal":true}],"paths":{"/api/me":{"get":{"tags":["Account and sign-in"],"operationId":"get_me","summary":"Who is signed in","description":"With a session: the user, workspace, role, features, plan and pending approvals. Without one: `user: null` and what the login screen needs. Ignores API keys.\n\n**Access:** No sign-in","security":[],"x-studio-auth":"public","responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"user":{"type":["object","null"],"description":"id, email, name, is_admin, role, can_manage, is_client, two_factor"},"workspaces":{"type":"array","items":{"type":"object"},"description":"id, slug, name, role"},"features":{"type":"object"},"modules":{"type":"object","description":"Per module whether it is on for the workspace (plan and setting)."},"plan":{"type":["object","null"],"description":"key, label, limits, usage, trial_ends_at, active, cancel_from, data_delete_on"},"approvals_open":{"type":"integer"},"terms":{"type":["object","null"],"description":"alleen voor de eigenaar / owner only: version, required, deadline, blocking"},"start_page":{"type":["string","null"]},"viewed_by":{"type":["object","null"]},"google_connect":{"type":"boolean"},"setup_required":{"type":"boolean"},"google_login":{"type":"boolean"},"signup_open":{"type":"boolean"},"brand":{"type":["object","null"]}}}}}}}}},"/api/login":{"post":{"tags":["Account and sign-in"],"operationId":"post_login","summary":"Sign in with email and password","description":"Sets the session cookie. With two-factor enabled, a `challenge` comes back first for `/api/login/two-factor`. The iOS and macOS app sends `client: app` and gets a `token` in the response instead of a cookie; it is sent as `Authorization: Bearer studioapp_…` and is listed under Account → Devices.\n\n**Access:** No sign-in","security":[],"x-studio-auth":"public","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","format":"email"},"password":{"type":"string"},"client":{"type":"string","enum":["app"],"description":"The app only."},"device_name":{"type":"string","description":"Name under Account → Devices; without one \"iPhone van <first name>\"."},"device_model":{"type":"string","description":"iPhone, iPad or Mac."}},"required":["email","password"]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"two_factor":{"type":"boolean"},"challenge":{"type":"string"},"token":{"type":"string","description":"Only with `client: app`, shown once."},"device_name":{"type":"string"}}}}}},"401":{"$ref":"#/components/responses/401"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/login/two-factor":{"post":{"tags":["Account and sign-in"],"operationId":"post_login_two_factor","summary":"Second step: authenticator code or recovery code","description":"The challenge expires after five wrong codes. When the sign-in started in the app, a `token` comes back instead of a cookie.\n\n**Access:** No sign-in","security":[],"x-studio-auth":"public","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"challenge":{"type":"string","description":"From the /api/login response; after Google it is in a cookie."},"code":{"type":"string"}},"required":["code"]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"token":{"type":["string","null"]},"device_name":{"type":["string","null"]}}}}}},"401":{"$ref":"#/components/responses/401"},"410":{"$ref":"#/components/responses/410"},"429":{"$ref":"#/components/responses/429"}}}},"/api/login/google":{"get":{"tags":["Account and sign-in"],"operationId":"get_login_google","summary":"Start Google sign-in","description":"Redirects the browser to Google (OpenID Connect with PKCE). Existing users only. 404 when Google sign-in isn't configured. The app starts with `?client=app&code_challenge=…` (S256, at least 43 characters) and returns to `instntstudio://login?code=…`, `?challenge=…` (two-factor) or `?error=…`.\n\n**Access:** No sign-in","security":[],"x-studio-auth":"public","responses":{"302":{"description":"Redirect; the address is in Location.","headers":{"Location":{"schema":{"type":"string","format":"uri"}}}}}}},"/api/login/app":{"post":{"tags":["Account and sign-in"],"operationId":"post_login_app","summary":"App: exchange the code from after Google","description":"Single use and valid for two minutes; a wrong verifier also uses up the code.\n\n**Access:** No sign-in","security":[],"x-studio-auth":"public","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string"},"code_verifier":{"type":"string","description":"Whose S256 hash the app sent at the start."}},"required":["code","code_verifier"]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"token":{"type":"string"},"device_name":{"type":"string"}}}}}},"410":{"$ref":"#/components/responses/410"},"429":{"$ref":"#/components/responses/429"}}}},"/api/login/google/callback":{"get":{"tags":["Account and sign-in"],"operationId":"get_login_google_callback","summary":"Return from Google","description":"Google sends the browser here. A small page sets the session and returns to the app.\n\n**Access:** No sign-in","security":[],"x-studio-auth":"public","responses":{"200":{"description":"Success.","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/api/logout":{"post":{"tags":["Account and sign-in"],"operationId":"post_logout","summary":"Sign out","description":"Ends the session and clears the cookie.\n\n**Access:** App session only, not with an API key","security":[{"session":[]}],"x-studio-auth":"session","responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/setup":{"post":{"tags":["Account and sign-in"],"operationId":"post_setup","summary":"Create the first user","description":"Only works while there are no users yet (installation); afterwards `403 already_installed`.\n\n**Access:** No sign-in","security":[],"x-studio-auth":"public","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","format":"email"},"name":{"type":"string"},"password":{"type":"string","description":"At least 10 characters."}},"required":["email","password"]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true}}}}}}}}},"/api/signup":{"post":{"tags":["Account and sign-in"],"operationId":"post_signup","summary":"Sign up as an agency (trial)","description":"Sends a confirmation email. Always returns `sent: true`, also when the address exists. 404 while sign-up is closed.\n\n**Access:** No sign-in","security":[],"x-studio-auth":"public","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","format":"email"},"name":{"type":"string"},"agency":{"type":"string"},"password":{"type":"string","description":"At least 10 characters."},"partner":{"type":"string","description":"Partner code."},"terms":{"type":"boolean","description":"Acceptance of the terms and data processing agreement; version and time are recorded."}},"required":["email","name","agency","password","terms"]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"sent":{"type":"boolean"}}}}}}}}},"/api/signup/confirm":{"post":{"tags":["Account and sign-in"],"operationId":"post_signup_confirm","summary":"Confirm a sign-up","description":"Creates the user and workspace and starts a session. `410 signup_expired` after 24 hours, `409 exists` when the address already has an account.\n\n**Access:** No sign-in","security":[],"x-studio-auth":"public","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string"}},"required":["token"]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true}}}}}}}}},"/api/invitation":{"get":{"tags":["Account and sign-in"],"operationId":"get_invitation","summary":"View an invitation","description":"**Access:** No sign-in","security":[],"x-studio-auth":"public","parameters":[{"name":"token","in":"query","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"email":{"type":"string","format":"email"},"workspace":{"type":"string"},"project":{"type":["string","null"]},"existing_user":{"type":"boolean"}}}}}},"410":{"$ref":"#/components/responses/410"}}},"post":{"tags":["Account and sign-in"],"operationId":"post_invitation","summary":"Accept an invitation","description":"New user: choose a name and password. Existing user: confirm with your own password, or without when already signed in as that user. You can belong to several workspaces. Starts a session in the inviting workspace.\n\n**Access:** No sign-in","security":[],"x-studio-auth":"public","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string"},"password":{"type":"string"},"name":{"type":"string"}},"required":["token","password"]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true}}}}}},"401":{"$ref":"#/components/responses/401"},"409":{"$ref":"#/components/responses/409"},"410":{"$ref":"#/components/responses/410"},"422":{"$ref":"#/components/responses/422"}}}},"/api/password/forgot":{"post":{"tags":["Account and sign-in"],"operationId":"post_password_forgot","summary":"Request a reset link","description":"Always answers the same, so it doesn't reveal which addresses have an account. The link works for 60 minutes.\n\n**Access:** No sign-in","security":[],"x-studio-auth":"public","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","format":"email"}},"required":["email"]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true}}}}}}}}},"/api/password/reset":{"post":{"tags":["Account and sign-in"],"operationId":"post_password_reset","summary":"Set a new password","description":"Ends all other sessions and starts a new one.\n\n**Access:** No sign-in","security":[],"x-studio-auth":"public","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string"},"password":{"type":"string","description":"At least 10 characters, at most 72 bytes, not from a known breach."}},"required":["token","password"]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true}}}}}},"410":{"$ref":"#/components/responses/410"},"422":{"$ref":"#/components/responses/422"}}}},"/api/password/change":{"post":{"tags":["Account and sign-in"],"operationId":"post_password_change","summary":"Change password","description":"Other sessions end; this one stays valid.\n\n**Who:** The signed-in user.\n\n**Access:** App session only, not with an API key","security":[{"session":[]}],"x-studio-auth":"session","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"current":{"type":"string"},"password":{"type":"string","description":"At least 10 characters, at most 72 bytes, not from a known breach."}},"required":["current","password"]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/two-factor":{"post":{"tags":["Account and sign-in"],"operationId":"post_two_factor","summary":"Manage two-factor authentication","description":"**Who:** The signed-in user.\n\n**Access:** App session only, not with an API key","security":[{"session":[]}],"x-studio-auth":"session","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"status"}},"required":["action"],"title":"status"},{"type":"object","properties":{"action":{"const":"setup"}},"required":["action"],"title":"setup","description":"New secret; inactive until `confirm`."},{"type":"object","properties":{"action":{"const":"confirm"},"code":{"type":"string"}},"required":["action","code"],"title":"confirm","description":"Enables two-factor and ends other sessions."},{"type":"object","properties":{"action":{"const":"disable"},"password":{"type":"string","description":"Required when you have a password."},"code":{"type":"string"}},"required":["action","code"],"title":"disable","description":"Not possible when two-factor is mandatory."},{"type":"object","properties":{"action":{"const":"recovery_codes"},"password":{"type":"string","description":"Required when you have a password."},"code":{"type":"string","description":"Required without a password."}},"required":["action"],"title":"recovery_codes"}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true},"enabled":{"type":"boolean"},"required":{"type":"boolean"},"enforced":{"type":"boolean","description":"Mandatory: admin, or the workspace requires it."},"has_password":{"type":"boolean"},"recovery_codes_left":{"type":"integer"}},"title":"status"},{"type":"object","properties":{"ok":{"const":true},"secret":{"type":"string"},"uri":{"type":"string"}},"title":"setup"},{"type":"object","properties":{"ok":{"const":true},"recovery_codes":{"type":"array","items":{"type":"string"}}},"title":"confirm"},{"type":"object","properties":{"ok":{"const":true}},"title":"disable"},{"type":"object","properties":{"ok":{"const":true},"recovery_codes":{"type":"array","items":{"type":"string"}}},"title":"recovery_codes"}]}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/tokens":{"get":{"tags":["Account and sign-in"],"operationId":"get_tokens","summary":"Your API keys","description":"**Who:** The signed-in user.\n\n**Access:** App session only, not with an API key","security":[{"session":[]}],"x-studio-auth":"session","responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"tokens":{"type":"array","items":{"type":"object"},"description":"id, name, scope, last_used_at, created_at, archived_at, workspace_id, workspace"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Account and sign-in"],"operationId":"post_tokens","summary":"Create or revoke an API key","description":"**Who:** The signed-in user.\n\n**Access:** App session only, not with an API key","security":[{"session":[]}],"x-studio-auth":"session","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"create"},"name":{"type":"string"},"scope":{"type":"string","enum":["mcp","read","write"],"description":"Defaults to mcp."}},"required":["action"],"title":"create","description":"The key only works in the workspace it was created in. An older key without a workspace picks one with the header `X-Workspace: <id>`."},{"type":"object","properties":{"action":{"const":"delete"},"id":{"type":"integer"}},"required":["action","id"],"title":"delete"}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true},"token":{"type":"string","description":"Shown once."}},"title":"create"},{"type":"object","properties":{"ok":{"const":true}},"title":"delete"}]}}}},"401":{"$ref":"#/components/responses/401"},"402":{"$ref":"#/components/responses/402"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/devices":{"get":{"tags":["Account and sign-in"],"operationId":"get_devices","summary":"Devices for push notifications","description":"**Who:** The signed-in user.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"devices":{"type":"array","items":{"type":"object"},"description":"id, environment, created_at, last_sent_at"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Account and sign-in"],"operationId":"post_devices","summary":"Register or remove a push device","description":"**Who:** The signed-in user.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"register"},"token":{"type":"string","description":"APNs device token, hex."},"environment":{"type":"string","enum":["sandbox","production"]}},"required":["action","token"],"title":"register"},{"type":"object","properties":{"action":{"const":"remove"},"token":{"type":"string"}},"required":["action","token"],"title":"remove"}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true}},"title":"register"},{"type":"object","properties":{"ok":{"const":true}},"title":"remove"}]}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/view-as":{"post":{"tags":["Account and sign-in"],"operationId":"post_view_as","summary":"View as a client","description":"While viewing, nothing can be changed.\n\n**Who:** Workspace owner only.\n\n**Access:** App session only, not with an API key","security":[{"session":[]}],"x-studio-auth":"session","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"start"},"user_id":{"type":"integer","description":"A client of your own workspace."}},"required":["action","user_id"],"title":"start"},{"type":"object","properties":{"action":{"const":"stop"}},"required":["action"],"title":"stop"}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true}},"title":"start"},{"type":"object","properties":{"ok":{"const":true}},"title":"stop"}]}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/profile":{"get":{"tags":["Account and sign-in"],"operationId":"get_profile","summary":"Your own account","description":"**Who:** The signed-in user.\n\n**Access:** App session only, not with an API key","security":[{"session":[]}],"x-studio-auth":"session","responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"profile":{"type":"object","description":"name, email, timezone, avatar, has_password, google, created_at"},"pending_email":{"type":["object","null"],"description":"email, expires_at"},"reauth":{"type":"string","enum":["password","code","google","none"],"description":"How you confirm your identity for a sensitive action."},"notify":{"type":"array","items":{"type":"object"},"description":"key, label, on"},"quiet_hours":{"type":["object","null"],"description":"Quiet hours for push: from and to, HH:MM in your own time zone."},"sessions":{"type":"array","items":{"type":"object"},"description":"key, kind (browser|app), device, network, via, created_at, last_used_at, last_used_label, current"},"workspaces":{"type":"array","items":{"type":"object"},"description":"id, name, role, suspended_at"},"delete_blockers":{"type":"array","items":{"type":"string"},"description":"Workspaces you own; while there are any, your account can't be deleted."},"timezones":{"type":"array","items":{"type":"string"}}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Account and sign-in"],"operationId":"post_profile","summary":"Change your account","description":"**Who:** The signed-in user.\n\n**Access:** App session only, not with an API key","security":[{"session":[]}],"x-studio-auth":"session","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"profile"},"name":{"type":"string"},"timezone":{"type":["string","null"],"description":"IANA name, e.g. Europe/Amsterdam. Used for times in emails and lists."}},"required":["action","name"],"title":"profile"},{"type":"object","properties":{"action":{"const":"avatar"},"avatar":{"type":"string","format":"binary","description":"JPG, PNG, WebP or GIF up to 3 MB; becomes a 160 × 160 WebP without metadata."}},"required":["action","avatar"],"title":"avatar","description":"Multipart form."},{"type":"object","properties":{"action":{"const":"avatar_delete"}},"required":["action"],"title":"avatar_delete"},{"type":"object","properties":{"action":{"const":"email"},"email":{"type":"string","format":"email"},"password":{"type":"string","description":"Your password; without one (Google only) a code from the app, or sign in with Google again first."},"code":{"type":"string"}},"required":["action","email"],"title":"email","description":"Sends a link to the new address; until it is opened, the old one stays."},{"type":"object","properties":{"action":{"const":"email_cancel"}},"required":["action"],"title":"email_cancel"},{"type":"object","properties":{"action":{"const":"notify"},"prefs":{"type":"object","description":"Per kind on or off: alerts, anomalies, digest, automations. Invoices and security can't be turned off."}},"required":["action","prefs"],"title":"notify"},{"type":"object","properties":{"action":{"const":"quiet_hours"},"from":{"type":"string","description":"HH:MM; without from and to, quiet hours are off."},"to":{"type":"string"}},"required":["action"],"title":"quiet_hours","description":"Push notifications then arrive silently."},{"type":"object","properties":{"action":{"const":"session_revoke"},"key":{"type":"string"}},"required":["action","key"],"title":"session_revoke","description":"For an app, also the push notifications to that device."},{"type":"object","properties":{"action":{"const":"sessions_revoke_others"}},"required":["action"],"title":"sessions_revoke_others"},{"type":"object","properties":{"action":{"const":"reauth"},"password":{"type":"string","description":"Your password; without one (Google only) a code from the app, or sign in with Google again first."},"code":{"type":"string"}},"required":["action"],"title":"reauth","description":"Confirms your identity for ten minutes."},{"type":"object","properties":{"action":{"const":"leave"},"workspace_id":{"type":"integer"}},"required":["action","workspace_id"],"title":"leave","description":"Not as owner: transfer ownership first."},{"type":"object","properties":{"action":{"const":"delete_account"},"password":{"type":"string","description":"Your password; without one (Google only) a code from the app, or sign in with Google again first."},"code":{"type":"string"}},"required":["action"],"title":"delete_account","description":"Anonymises your account and signs you out. Not while you own a workspace."}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true}},"title":"profile"},{"type":"object","properties":{"ok":{"const":true},"avatar":{"type":"string"}},"title":"avatar"},{"type":"object","properties":{"ok":{"const":true}},"title":"avatar_delete"},{"type":"object","properties":{"ok":{"const":true},"mailed":{"type":"boolean"},"pending_email":{"type":["object","null"],"description":"email, expires_at"}},"title":"email"},{"type":"object","properties":{"ok":{"const":true}},"title":"email_cancel"},{"type":"object","properties":{"ok":{"const":true},"prefs":{"type":"object"}},"title":"notify"},{"type":"object","properties":{"ok":{"const":true},"quiet_hours":{"type":["object","null"],"description":"from, to"}},"title":"quiet_hours"},{"type":"object","properties":{"ok":{"const":true},"sessions":{"type":"array","items":{"type":"object"}}},"title":"session_revoke"},{"type":"object","properties":{"ok":{"const":true},"revoked":{"type":"integer"},"sessions":{"type":"array","items":{"type":"object"}}},"title":"sessions_revoke_others"},{"type":"object","properties":{"ok":{"const":true}},"title":"reauth"},{"type":"object","properties":{"ok":{"const":true}},"title":"leave"},{"type":"object","properties":{"ok":{"const":true}},"title":"delete_account"}]}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/email/confirm":{"post":{"tags":["Account and sign-in"],"operationId":"post_email_confirm","summary":"Confirm a new email address","description":"The link from the email to the new address (24 hours). The old address gets an email with a link to undo.\n\n**Access:** No sign-in; the secret is in the address","security":[],"x-studio-auth":"url","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string"}},"required":["token"]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"email":{"type":"string","format":"email"}}}}}},"404":{"$ref":"#/components/responses/404"},"410":{"$ref":"#/components/responses/410"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/email/undo":{"post":{"tags":["Account and sign-in"],"operationId":"post_email_undo","summary":"Undo an email change","description":"The link from the email to the old address (24 hours). Restores the old address, ends all sessions and revokes API keys.\n\n**Access:** No sign-in; the secret is in the address","security":[],"x-studio-auth":"url","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string"}},"required":["token"]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"email":{"type":"string","format":"email"}}}}}},"404":{"$ref":"#/components/responses/404"},"410":{"$ref":"#/components/responses/410"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/access":{"get":{"tags":["Workspace, team and billing"],"operationId":"get_access","summary":"Team, clients and invitations","description":"**Who:** Owner or member; viewers and clients get 403.\n\n**Access:** App session only, not with an API key","security":[{"session":[]}],"x-studio-auth":"session","parameters":[{"name":"project_id","in":"query","required":false,"schema":{"type":"integer"},"description":"With a project: that project's clients. Without: the team."}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"team":{"type":"array","items":{"type":"object"},"description":"id, email, name, role, last_login_at, two_factor, avatar, archived"},"clients":{"type":"array","items":{"type":"object"},"description":"id, email, name, last_login_at, projects"},"require_two_factor":{"type":"boolean"},"is_owner":{"type":"boolean"},"me":{"type":"integer"},"people":{"type":"array","items":{"type":"object"},"description":"id, email, name, preset, permissions, last_login_at, since"},"invitations":{"type":"array","items":{"type":"object"},"description":"id, email, role, preset, expires_at, created_at"},"presets":{"type":"array","items":{"type":"object"},"description":"id, label, permissions"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Workspace, team and billing"],"operationId":"post_access","summary":"Invite people and manage access","description":"**Who:** Owner or member; viewers and clients get 403.\n\n**Access:** App session only, not with an API key","security":[{"session":[]}],"x-studio-auth":"session","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"invite"},"email":{"type":"string","format":"email"},"role":{"type":"string","enum":["client","member","viewer"],"description":"Defaults to client. Only the owner can invite members or viewers."},"project_id":{"type":"integer","description":"Required for a client."},"preset":{"type":"string","enum":["client_readonly","client_approver","client_limited"]}},"required":["action","email"],"title":"invite","description":"Someone who already belongs to another agency can be invited too; they then switch between workspaces."},{"type":"object","properties":{"action":{"const":"update"},"project_id":{"type":"integer"},"user_id":{"type":"integer"},"preset":{"type":"string","enum":["client_readonly","client_approver","client_limited"]}},"required":["action","project_id","user_id","preset"],"title":"update"},{"type":"object","properties":{"action":{"const":"remove"},"project_id":{"type":"integer"},"user_id":{"type":"integer"}},"required":["action","project_id","user_id"],"title":"remove","description":"The client loses access to this project."},{"type":"object","properties":{"action":{"const":"revoke"},"invitation_id":{"type":"integer"}},"required":["action","invitation_id"],"title":"revoke"},{"type":"object","properties":{"action":{"const":"resend"},"invitation_id":{"type":"integer"}},"required":["action","invitation_id"],"title":"resend","description":"New link (the old one stops working), valid for 14 days again."},{"type":"object","properties":{"action":{"const":"remove_member"},"user_id":{"type":"integer"}},"required":["action","user_id"],"title":"remove_member","description":"Removes someone from the workspace, with an email. The owner for team members; owner or member for clients."},{"type":"object","properties":{"action":{"const":"change_role"},"user_id":{"type":"integer"},"role":{"type":"string","enum":["member","viewer"]}},"required":["action","user_id","role"],"title":"change_role","description":"Workspace owner only."},{"type":"object","properties":{"action":{"const":"transfer"},"user_id":{"type":"integer","description":"A team member."},"password":{"type":"string","description":"Your password; without one (Google only) a code from the app, or sign in with Google again first."},"code":{"type":"string"}},"required":["action","user_id"],"title":"transfer","description":"One owner per workspace: the new owner takes over, you become a member. Both get an email. Workspace owner only."},{"type":"object","properties":{"action":{"const":"require_two_factor"},"on":{"type":"boolean"}},"required":["action","on"],"title":"require_two_factor","description":"Team members without two-factor can then only set it up. Enable it yourself first. Workspace owner only."}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true},"invitation_id":{"type":"integer"},"link":{"type":"string","format":"uri","description":"The invitation link, also when the email doesn't arrive."},"mailed":{"type":"boolean"},"granted":{"type":"boolean","description":"Already a client of this agency: access granted at once, no invitation."}},"title":"invite"},{"type":"object","properties":{"ok":{"const":true}},"title":"update"},{"type":"object","properties":{"ok":{"const":true}},"title":"remove"},{"type":"object","properties":{"ok":{"const":true}},"title":"revoke"},{"type":"object","properties":{"ok":{"const":true},"link":{"type":"string","format":"uri"},"mailed":{"type":"boolean"}},"title":"resend"},{"type":"object","properties":{"ok":{"const":true}},"title":"remove_member"},{"type":"object","properties":{"ok":{"const":true}},"title":"change_role"},{"type":"object","properties":{"ok":{"const":true}},"title":"transfer"},{"type":"object","properties":{"ok":{"const":true}},"title":"require_two_factor"}]}}}},"401":{"$ref":"#/components/responses/401"},"402":{"$ref":"#/components/responses/402"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/workspace/export":{"get":{"tags":["Workspace, team and billing"],"operationId":"get_workspace_export","summary":"Export all workspace data","description":"A JSON file download. Also works with an owner's read key.\n\n**Who:** Workspace owner only.\n\n**Access:** App session only, not with an API key","security":[{"session":[]}],"x-studio-auth":"session","responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true}},"additionalProperties":true}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/workspace/delete":{"post":{"tags":["Workspace, team and billing"],"operationId":"post_workspace_delete","summary":"Delete the workspace","description":"Requires the workspace name and your identity. `409 has_invoices` while there are invoices (retention duty).\n\n**Who:** Workspace owner only.\n\n**Access:** App session only, not with an API key","security":[{"session":[]}],"x-studio-auth":"session","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"password":{"type":"string","description":"Your password; without one (Google only) a code from the app, or sign in with Google again first."},"code":{"type":"string"}},"required":["name"]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/workspace/switch":{"post":{"tags":["Workspace, team and billing"],"operationId":"post_workspace_switch","summary":"Switch workspace","description":"Applies to this session and is remembered for the next sign-in. Only a workspace you belong to.\n\n**Who:** The signed-in user.\n\n**Access:** App session only, not with an API key","security":[{"session":[]}],"x-studio-auth":"session","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"workspace_id":{"type":"integer"}},"required":["workspace_id"]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"workspace_id":{"type":"integer"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/workspace/create":{"post":{"tags":["Workspace, team and billing"],"operationId":"post_workspace_create","summary":"New workspace","description":"You become the owner. With a trial, unless you already had one as an owner; then you choose a plan straight away.\n\n**Who:** The signed-in user.\n\n**Access:** App session only, not with an API key","security":[{"session":[]}],"x-studio-auth":"session","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"}},"required":["name"]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"workspace_id":{"type":"integer"},"trial":{"type":"boolean"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/workspace/rename":{"post":{"tags":["Workspace, team and billing"],"operationId":"post_workspace_rename","summary":"Rename the workspace","description":"Owner only, not with an API key.\n\n**Who:** Workspace owner only.\n\n**Access:** App session only, not with an API key","security":[{"session":[]}],"x-studio-auth":"session","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"}},"required":["name"]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"name":{"type":"string"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/logboek":{"get":{"tags":["Workspace, team and billing"],"operationId":"get_logboek","summary":"Workspace audit log","description":"Who did what, two years back. With `format=csv` as a file (at most 10,000 rows).\n\n**Who:** Workspace owner only.\n\n**Access:** App session only, not with an API key","security":[{"session":[]}],"x-studio-auth":"session","parameters":[{"name":"user_id","in":"query","required":false,"schema":{"type":"integer"}},{"name":"group","in":"query","required":false,"schema":{"type":"string","enum":["toegang","account","abonnement","werk","koppelingen","mail"]}},{"name":"from","in":"query","required":false,"schema":{"type":"string","format":"date"}},{"name":"to","in":"query","required":false,"schema":{"type":"string","format":"date"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer"}},{"name":"format","in":"query","required":false,"schema":{"type":"string","enum":["csv"]}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"entries":{"type":"array","items":{"type":"object"},"description":"id, at, action, label, subject, user, details"},"total":{"type":"integer"},"page":{"type":"integer"},"per_page":{"type":"integer"},"retention_days":{"type":"integer"},"groups":{"type":"array","items":{"type":"object"},"description":"key, label"},"people":{"type":"array","items":{"type":"object"},"description":"id, name, email"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/branding":{"get":{"tags":["Workspace, team and billing"],"operationId":"get_branding","summary":"Branding of the agency and a project","description":"**Who:** Team only (owner, member or viewer); clients get 403.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"project_id","in":"query","required":false,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"workspace":{"type":"object","description":"logo, color, custom_domain, domain_status"},"project":{"type":["object","null"],"description":"logo, color"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Workspace, team and billing"],"operationId":"post_branding","summary":"Set branding and a custom domain","description":"**Who:** Owner or member; viewers and clients get 403.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"save"},"project_id":{"type":"integer","description":"Without a project: the agency (owner only)."},"logo":{"type":"string","description":"PNG, JPEG or WebP as base64 or data URL, at most 300 kB."},"color":{"type":"string"},"remove_logo":{"type":"boolean"}},"required":["action"],"title":"save"},{"type":"object","properties":{"action":{"const":"domain"},"domain":{"type":"string","description":"A subdomain of your own domain; empty removes it."}},"required":["action"],"title":"domain","description":"Workspace owner only."},{"type":"object","properties":{"action":{"const":"domain_check"}},"required":["action"],"title":"domain_check","description":"Workspace owner only."}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true}},"title":"save"},{"type":"object","properties":{"ok":{"const":true},"domain":{"type":["string","null"]},"status":{"type":"string"},"target":{"type":"string"}},"title":"domain"},{"type":"object","properties":{"ok":{"const":true},"domain":{"type":["string","null"]},"status":{"type":"string"},"target":{"type":"string"}},"title":"domain_check"}]}}}},"401":{"$ref":"#/components/responses/401"},"402":{"$ref":"#/components/responses/402"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/billing":{"get":{"tags":["Workspace, team and billing"],"operationId":"get_billing","summary":"Subscription, billing details and invoices","description":"**Who:** Workspace owner only.\n\n**Access:** App session only, not with an API key","security":[{"session":[]}],"x-studio-auth":"session","responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"details":{"type":["object","null"],"description":"name, address, postcode, city, country, vat_number, email, cc_email, reference"},"interval":{"type":"string"},"yearly_from":{"type":["string","null"],"format":"date"},"price":{"type":["object","null"],"description":"month, year, label"},"exempt":{"type":"boolean"},"vat":{"type":["object","null"],"description":"verified, checked_at"},"payments":{"type":"object","description":"online, mode (off, test, live, wrong)"},"mandate":{"type":"object","description":"active, method (directdebit, creditcard), details (gemaskeerd / masked), status, methods"},"suspended":{"type":["string","null"]},"invoices":{"type":"array","items":{"type":"object"},"description":"id, number, period, period_label, issued_on, due_on, gross_cents, refunded_cents, credited_cents, due_cents, status (open, paid, refunded, chargeback, credited, credit), last_payment_status, credit_of, credit_of_number, paid_at, pay_url"},"subscription":{"type":"object","description":"state (proef, actief, opzegging, opgezegd, verlopen, geschorst, gepauzeerd), plan, label, interval, trial_ends_at, paid_until, term, cancel_from, pending, data_delete_on, data_purged_at, pause (?object: since, until, cancelled), pause_blocked (?string), pause_max_months"},"plans":{"type":"array","items":{"type":"object"},"description":"key, label, price, price_year, price_next (?object: from, price, price_year), pitch, limits, ads_modes, white_label, over (wat niet past / what does not fit)"},"usage":{"type":"object"},"addons":{"type":"array","items":{"type":"object"},"description":"key, label, price (euro per maand per stuk / euro per month each), limit, amount, quantity, available"},"archived":{"type":"object","description":"projects, team, keywords, dashboards, api_tokens, automations"},"upcoming":{"type":"array","items":{"type":"object"},"description":"date, period_label, lines, net_cents, vat_cents, gross_cents"},"history":{"type":"array","items":{"type":"object"},"description":"id, text, kind, pending, note, created_at"},"terms":{"type":"object","description":"version, accepted_version, accepted_at, required, deadline, blocking, terms_url, dpa_url"},"has_password":{"type":"boolean"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Workspace, team and billing"],"operationId":"post_billing","summary":"Change or cancel the subscription, billing details","description":"Larger plan: immediately, per day or the difference for the rest of the year. Smaller plan or back to monthly: after the paid period. A smaller plan, cancelling and pausing require re-authentication: `password`, or without a password a `code` from the authenticator or a fresh Google login (at most ten minutes old).\n\n**Who:** Workspace owner only.\n\n**Access:** App session only, not with an API key","security":[{"session":[]}],"x-studio-auth":"session","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"details"},"name":{"type":"string"},"address":{"type":"string"},"postcode":{"type":"string"},"city":{"type":"string"},"country":{"type":"string","description":"Country code, defaults to NL."},"vat_number":{"type":"string","description":"Outside NL within the EU checked with VIES; only then is VAT reverse-charged."},"email":{"type":"string","format":"email"},"cc_email":{"type":"string","format":"email","description":"Receives a copy of every invoice and reminder."},"reference":{"type":"string","description":"Own reference (purchase order) on the invoice."}},"required":["action","name","address","postcode","city","email"],"title":"details"},{"type":"object","properties":{"action":{"const":"yearly"}},"required":["action"],"title":"yearly","description":"Pay yearly from the first of next month."},{"type":"object","properties":{"action":{"const":"preview"},"plan":{"type":"string","enum":["solo","inzicht","copiloot","begeleid"]},"interval":{"type":"string","enum":["maand","jaar"]}},"required":["action","plan","interval"],"title":"preview","description":"What would happen, without changing anything."},{"type":"object","properties":{"action":{"const":"change"},"plan":{"type":"string","enum":["solo","inzicht","copiloot","begeleid"]},"interval":{"type":"string","enum":["maand","jaar"]},"archive":{"type":"object","description":"Per kind the ids to set aside (projects, team, clients, api_tokens, automations, keywords:<project>, dashboards:<project>)."},"password":{"type":"string","description":"For a smaller plan: your password, or without one a code from the app (or sign in with Google again first)."},"code":{"type":"string"}},"required":["action","plan","interval"],"title":"change"},{"type":"object","properties":{"action":{"const":"cancel"},"password":{"type":"string","description":"Your password, or without one a code from the app (or sign in with Google again first)."},"code":{"type":"string"}},"required":["action"],"title":"cancel","description":"Cancel at the end of the paid period."},{"type":"object","properties":{"action":{"const":"resume"}},"required":["action"],"title":"resume","description":"Withdraw a cancellation that has not taken effect."},{"type":"object","properties":{"action":{"const":"withdraw"}},"required":["action"],"title":"withdraw","description":"Withdraw a scheduled change (smaller plan, other billing interval); everything stays as it is. Not for a cancellation, a pause or a change made by the admin."},{"type":"object","properties":{"action":{"const":"restore"},"kind":{"type":"string","enum":["projects","team","keywords","dashboards","api_tokens","automations"]},"id":{"type":"string"}},"required":["action","kind","id"],"title":"restore","description":"Restore something that was set aside, if the plan has room."},{"type":"object","properties":{"action":{"const":"terms"}},"required":["action"],"title":"terms","description":"Accept the current terms and data processing agreement."},{"type":"object","properties":{"action":{"const":"mandate_setup"},"method":{"type":"string","enum":["ideal","bancontact","creditcard"]}},"required":["action","method"],"title":"mandate_setup","description":"Automatic payment: a first payment at Mollie creates a mandate. With an open invoice that is the first payment; otherwise € 0.00 (credit card) or € 0.01 that is refunded immediately."},{"type":"object","properties":{"action":{"const":"mandate_revoke"}},"required":["action"],"title":"mandate_revoke","description":"Turn off automatic payment; invoices come with a pay link again."},{"type":"object","properties":{"action":{"const":"pause"},"months":{"type":"integer","description":"1 to 3."},"password":{"type":"string","description":"Or `code` / a fresh Google login, see re-authentication."}},"required":["action","months"],"title":"pause","description":"Pause from today: no charges, read-only, no background work. Monthly billing only, once per twelve months."},{"type":"object","properties":{"action":{"const":"end_pause"}},"required":["action"],"title":"end_pause","description":"End the pause now; the subscription resumes today."},{"type":"object","properties":{"action":{"const":"addon"},"addon":{"type":"string","description":"Key from `addons`."},"quantity":{"type":"integer","description":"0 to 20; 0 removes it."}},"required":["action","addon","quantity"],"title":"addon","description":"Extra room on top of the plan, billed per day on the monthly invoice from today. Lowering only if everything still fits."}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true},"vat":{"type":"string","description":"valid, invalid, unavailable, n.v.t."},"message":{"type":["string","null"]}},"title":"details"},{"type":"object","properties":{"ok":{"const":true},"yearly_from":{"type":"string","format":"date"},"message":{"type":"string"}},"title":"yearly"},{"type":"object","properties":{"ok":{"const":true},"preview":{"type":"object","description":"rows, effective_from, immediate, kind, message, over, upgrade_cents, replaces"}},"title":"preview"},{"type":"object","properties":{"ok":{"const":true},"message":{"type":"string"},"effective_from":{"type":"string","format":"date"}},"title":"change"},{"type":"object","properties":{"ok":{"const":true},"ends_on":{"type":"string","format":"date"},"message":{"type":"string"}},"title":"cancel"},{"type":"object","properties":{"ok":{"const":true},"message":{"type":"string"}},"title":"resume"},{"type":"object","properties":{"ok":{"const":true},"message":{"type":"string"}},"title":"withdraw"},{"type":"object","properties":{"ok":{"const":true},"message":{"type":"string"}},"title":"restore"},{"type":"object","properties":{"ok":{"const":true},"terms":{"type":"object"}},"title":"terms"},{"type":"object","properties":{"ok":{"const":true},"checkout_url":{"type":"string","format":"uri"}},"title":"mandate_setup"},{"type":"object","properties":{"ok":{"const":true},"message":{"type":"string"}},"title":"mandate_revoke"},{"type":"object","properties":{"ok":{"const":true},"until":{"type":"string","format":"date"},"message":{"type":"string"}},"title":"pause"},{"type":"object","properties":{"ok":{"const":true},"message":{"type":"string"}},"title":"end_pause"},{"type":"object","properties":{"ok":{"const":true},"message":{"type":"string"},"addons":{"type":"array","items":{"type":"object"}}},"title":"addon"}]}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/billing/pdf":{"get":{"tags":["Workspace, team and billing"],"operationId":"get_billing_pdf","summary":"Invoice as PDF","description":"**Who:** Workspace owner only.\n\n**Access:** App session only, not with an API key","security":[{"session":[]}],"x-studio-auth":"session","parameters":[{"name":"id","in":"query","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Success.","content":{"application/pdf":{"schema":{"type":"string","format":"binary"}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/partner":{"get":{"tags":["Workspace, team and billing"],"operationId":"get_partner","summary":"Partner programme: code, referred agencies and commission","description":"**Who:** Workspace owner only.\n\n**Access:** App session only, not with an API key","security":[{"session":[]}],"x-studio-auth":"session","responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"partner":{"type":["object","null"],"description":"code, commission_pct, discount_pct, active, link, referred, commission"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/werkwijzen":{"get":{"tags":["Workspace, team and billing"],"operationId":"get_werkwijzen","summary":"The team's own standards","description":"**Who:** Team only (owner, member or viewer); clients get 403.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"slug","in":"query","required":false,"schema":{"type":"string"},"description":"One standard as HTML."}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"standards":{"type":"array","items":{"type":"object"}},"standard":{"type":"object"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/admin":{"get":{"tags":["Workspace, team and billing"],"operationId":"get_admin","summary":"Admin: workspaces, users, queue, plans","description":"**Who:** instnt platform admins only; others get 404 or 403.\n\n**Access:** App session only, not with an API key","security":[{"session":[]}],"x-studio-auth":"session","responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"workspaces":{"type":"array","items":{"type":"object"}},"users":{"type":"array","items":{"type":"object"}},"queue":{"type":"object","description":"pending, oldest, failed"},"tables":{"type":"array","items":{"type":"object"}},"flags":{"type":"object"},"plans":{"type":"object"},"backup":{"type":["object","null"]},"plesk":{"type":"object"},"partners":{"type":"array","items":{"type":"object"}}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Workspace, team and billing"],"operationId":"post_admin","summary":"Admin actions","description":"**Who:** instnt platform admins only; others get 404 or 403.\n\n**Access:** App session only, not with an API key","security":[{"session":[]}],"x-studio-auth":"session","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"feature"},"workspace_id":{"type":"integer"},"feature":{"type":"string"},"state":{"type":"string","enum":["aan","uit","standaard"]}},"required":["action","workspace_id","feature","state"],"title":"feature"},{"type":"object","properties":{"action":{"const":"retry"},"id":{"type":"integer","description":"Failed job."}},"required":["action","id"],"title":"retry"},{"type":"object","properties":{"action":{"const":"forget"},"id":{"type":"integer"}},"required":["action","id"],"title":"forget"},{"type":"object","properties":{"action":{"const":"plan"},"workspace_id":{"type":"integer"},"plan":{"type":"string"},"trial_days":{"type":"integer"}},"required":["action","workspace_id","plan"],"title":"plan"},{"type":"object","properties":{"action":{"const":"interval"},"workspace_id":{"type":"integer"},"interval":{"type":"string","enum":["maand","jaar"]},"from":{"type":"string","format":"date","description":"The first of a month."}},"required":["action","workspace_id","interval"],"title":"interval"},{"type":"object","properties":{"action":{"const":"trial_extend"},"workspace_id":{"type":"integer"},"days":{"type":"integer","description":"1–90, default 7; from the current end date."}},"required":["action","workspace_id"],"title":"trial_extend"},{"type":"object","properties":{"action":{"const":"data_hold"},"workspace_id":{"type":"integer"},"until":{"type":["string","null"],"format":"date","description":"Postpone the clean-up until this date; empty lifts it."}},"required":["action","workspace_id"],"title":"data_hold"},{"type":"object","properties":{"action":{"const":"partner"},"workspace_id":{"type":"integer"},"code":{"type":"string"},"commission_pct":{"type":"integer"},"discount_pct":{"type":"integer"},"active":{"type":"boolean"}},"required":["action","workspace_id","code"],"title":"partner"},{"type":"object","properties":{"action":{"const":"commission_paid"},"invoice_id":{"type":"integer"}},"required":["action","invoice_id"],"title":"commission_paid"},{"type":"object","properties":{"action":{"const":"mark_paid"},"invoice_id":{"type":"integer"},"paid_on":{"type":"string","format":"date","description":"Between invoice date and today."},"reference":{"type":"string"}},"required":["action","invoice_id"],"title":"mark_paid","description":"Paid by bank transfer; lifts a suspension."},{"type":"object","properties":{"action":{"const":"credit"},"invoice_id":{"type":"integer"},"amount_cents":{"type":"integer","description":"Excluding VAT; empty credits the whole invoice."},"reason":{"type":"string"},"refund":{"type":"boolean","description":"Also refund through Mollie."}},"required":["action","invoice_id","reason"],"title":"credit"},{"type":"object","properties":{"action":{"const":"remind"},"invoice_id":{"type":"integer"}},"required":["action","invoice_id"],"title":"remind"},{"type":"object","properties":{"action":{"const":"discount"},"workspace_id":{"type":"integer"},"kind":{"type":"string","enum":["pct","fixed"]},"value":{"type":"integer","description":"Percent (1–100) or cents per invoice."},"months":{"type":"integer"},"note":{"type":"string"},"starts_on":{"type":"string","format":"date"}},"required":["action","workspace_id","kind","value","note"],"title":"discount"},{"type":"object","properties":{"action":{"const":"discount_end"},"id":{"type":"integer"}},"required":["action","id"],"title":"discount_end"},{"type":"object","properties":{"action":{"const":"close_workspace"},"workspace_id":{"type":"integer"},"reason":{"type":"string"}},"required":["action","workspace_id"],"title":"close_workspace"},{"type":"object","properties":{"action":{"const":"unsuspend"},"workspace_id":{"type":"integer"}},"required":["action","workspace_id"],"title":"unsuspend"},{"type":"object","properties":{"action":{"const":"end_pause"},"workspace_id":{"type":"integer"}},"required":["action","workspace_id"],"title":"end_pause","description":"End a pause; the customer gets an email."},{"type":"object","properties":{"action":{"const":"price_schedule"},"plan":{"type":"string"},"price":{"type":"number"},"price_year":{"type":"number"},"from":{"type":"string","format":"date","description":"At least 30 days ahead; paying customers on that plan get an email."}},"required":["action","plan","price","price_year","from"],"title":"price_schedule"},{"type":"object","properties":{"action":{"const":"price_cancel"},"id":{"type":"integer"}},"required":["action","id"],"title":"price_cancel"},{"type":"object","properties":{"action":{"const":"partner_payout_info"},"workspace_id":{"type":"integer"},"name":{"type":"string"},"iban":{"type":"string"}},"required":["action","workspace_id"],"title":"partner_payout_info"},{"type":"object","properties":{"action":{"const":"payout"}},"required":["action"],"title":"payout","description":"All open commission of paid invoices in one payout."},{"type":"object","properties":{"action":{"const":"payout_paid"},"id":{"type":"integer"}},"required":["action","id"],"title":"payout_paid"},{"type":"object","properties":{"action":{"const":"plesk_add"},"url":{"type":"string","format":"uri"},"api_key":{"type":"string"},"name":{"type":"string"}},"required":["action","url","api_key"],"title":"plesk_add"},{"type":"object","properties":{"action":{"const":"plesk_check"},"id":{"type":"integer"}},"required":["action","id"],"title":"plesk_check"},{"type":"object","properties":{"action":{"const":"plesk_remove"},"id":{"type":"integer"}},"required":["action","id"],"title":"plesk_remove"}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true}},"title":"feature"},{"type":"object","properties":{"ok":{"const":true}},"title":"retry"},{"type":"object","properties":{"ok":{"const":true}},"title":"forget"},{"type":"object","properties":{"ok":{"const":true}},"title":"plan"},{"type":"object","properties":{"ok":{"const":true}},"title":"interval"},{"type":"object","properties":{"ok":{"const":true},"trial_ends_at":{"type":"string","format":"date-time"}},"title":"trial_extend"},{"type":"object","properties":{"ok":{"const":true}},"title":"data_hold"},{"type":"object","properties":{"ok":{"const":true}},"title":"partner"},{"type":"object","properties":{"ok":{"const":true},"updated":{"type":"integer"}},"title":"commission_paid"},{"type":"object","properties":{"ok":{"const":true}},"title":"mark_paid"},{"type":"object","properties":{"ok":{"const":true},"number":{"type":"string"},"gross_cents":{"type":"integer"}},"title":"credit"},{"type":"object","properties":{"ok":{"const":true}},"title":"remind"},{"type":"object","properties":{"ok":{"const":true}},"title":"discount"},{"type":"object","properties":{"ok":{"const":true}},"title":"discount_end"},{"type":"object","properties":{"ok":{"const":true}},"title":"close_workspace"},{"type":"object","properties":{"ok":{"const":true}},"title":"unsuspend"},{"type":"object","properties":{"ok":{"const":true}},"title":"end_pause"},{"type":"object","properties":{"ok":{"const":true},"announced":{"type":"integer"}},"title":"price_schedule"},{"type":"object","properties":{"ok":{"const":true}},"title":"price_cancel"},{"type":"object","properties":{"ok":{"const":true}},"title":"partner_payout_info"},{"type":"object","properties":{"ok":{"const":true},"id":{"type":"integer"}},"title":"payout"},{"type":"object","properties":{"ok":{"const":true}},"title":"payout_paid"},{"type":"object","properties":{"ok":{"const":true},"id":{"type":"integer"},"domains":{"type":["integer","null"]},"error":{"type":["string","null"]}},"title":"plesk_add"},{"type":"object","properties":{"ok":{"const":true},"domains":{"type":"integer"}},"title":"plesk_check"},{"type":"object","properties":{"ok":{"const":true}},"title":"plesk_remove"}]}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/admin/invoices":{"get":{"tags":["Workspace, team and billing"],"operationId":"get_admin_invoices","summary":"Admin: all invoices and credit notes","description":"**Who:** instnt platform admins only; others get 404 or 403.\n\n**Access:** App session only, not with an API key","security":[{"session":[]}],"x-studio-auth":"session","parameters":[{"name":"status","in":"query","required":false,"schema":{"type":"string"},"description":"open, paid, credit, … or te_laat (overdue)."},{"name":"period","in":"query","required":false,"schema":{"type":"string"},"description":"yyyy or yyyy-mm (invoice date)."},{"name":"workspace_id","in":"query","required":false,"schema":{"type":"integer"}},{"name":"q","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"invoices":{"type":"array","items":{"type":"object"}},"totals":{"type":"object","description":"count, net_cents, vat_cents, gross_cents, open_cents, late_cents"},"workspaces":{"type":"array","items":{"type":"object"}}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/admin/invoice/pdf":{"get":{"tags":["Workspace, team and billing"],"operationId":"get_admin_invoice_pdf","summary":"Admin: invoice or credit note as PDF","description":"**Who:** instnt platform admins only; others get 404 or 403.\n\n**Access:** App session only, not with an API key","security":[{"session":[]}],"x-studio-auth":"session","parameters":[{"name":"id","in":"query","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Success.","content":{"application/pdf":{"schema":{"type":"string","format":"binary"}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/admin/export":{"get":{"tags":["Workspace, team and billing"],"operationId":"get_admin_export","summary":"Admin: CSV for bookkeeping, EC sales list, partner payout","description":"**Who:** instnt platform admins only; others get 404 or 403.\n\n**Access:** App session only, not with an API key","security":[{"session":[]}],"x-studio-auth":"session","parameters":[{"name":"kind","in":"query","required":true,"schema":{"type":"string","enum":["invoices","icp","payout"]}},{"name":"from","in":"query","required":false,"schema":{"type":"string","format":"date"}},{"name":"to","in":"query","required":false,"schema":{"type":"string","format":"date"}},{"name":"quarter","in":"query","required":false,"schema":{"type":"string"},"description":"As 2026-Q3."},{"name":"id","in":"query","required":false,"schema":{"type":"integer"},"description":"Payout."}],"responses":{"200":{"description":"Success.","content":{"text/csv":{"schema":{"type":"string"}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/projects":{"get":{"tags":["Projects and overview"],"operationId":"get_projects","summary":"Projects with source counts","description":"**Who:** Everyone in the workspace; a client only sees their own projects.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"projects":{"type":"array","items":{"type":"object"},"description":"id, name, color, website (het ingestelde adres, of afgeleid uit uptime of Search Console), website_set, position (eigen volgorde van de gebruiker, of null), brand_terms, template, conversion_value, property_count, app_source_count, search_console_count, access"},"templates":{"type":"object"}}},"example":{"ok":true,"projects":[{"id":5,"name":"Bakkerij Jansen","color":"#3552c9","brand_terms":"jansen","template":"lokaal","conversion_value":45,"property_count":1,"app_source_count":0,"search_console_count":1,"access":{"overview":"manage","website":"manage","search":"manage","product":"manage","advice":"manage","ads":"manage"}}],"templates":{}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Projects and overview"],"operationId":"post_projects","summary":"Create, edit and organise projects","description":"**Who:** Owner or member; viewers and clients get 403.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"create"},"name":{"type":"string"},"template":{"type":"string","enum":["webshop","saas","lokaal","b2b","content"],"description":"Required since 2026-10-06: the overview picks its key figures by project type."}},"required":["action","name","template"],"title":"create"},{"type":"object","properties":{"action":{"const":"rename"},"project_id":{"type":"integer"},"name":{"type":"string"}},"required":["action","project_id","name"],"title":"rename"},{"type":"object","properties":{"action":{"const":"update"},"project_id":{"type":"integer"},"name":{"type":"string"},"color":{"type":"string","description":"As #rrggbb."},"conversion_value":{"type":["number","null"]},"template":{"type":["string","null"]},"brand_terms":{"type":"string","description":"Comma-separated."},"website":{"type":"string","description":"Site address; empty clears it. A different domain moves uptime checks and site checks along."}},"required":["action","project_id","name","color"],"title":"update"},{"type":"object","properties":{"action":{"const":"delete"},"project_id":{"type":"integer"}},"required":["action","project_id"],"title":"delete","description":"Sources remain and become unassigned."},{"type":"object","properties":{"action":{"const":"assign"},"property_id":{"type":"integer","description":"GA4 property."},"project_id":{"type":["integer","null"]}},"required":["action","property_id"],"title":"assign"},{"type":"object","properties":{"action":{"const":"rename_property"},"property_id":{"type":"integer"},"name":{"type":"string","description":"Empty restores the name from Google."}},"required":["action","property_id"],"title":"rename_property"}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true},"project_id":{"type":"integer"}},"title":"create"},{"type":"object","properties":{"ok":{"const":true}},"title":"rename"},{"type":"object","properties":{"ok":{"const":true},"website":{"type":["object","null"],"description":"moved (int), search_console (object[]: id, site_url; hoort nog bij het oude domein)"}},"title":"update"},{"type":"object","properties":{"ok":{"const":true}},"title":"delete"},{"type":"object","properties":{"ok":{"const":true}},"title":"assign"},{"type":"object","properties":{"ok":{"const":true}},"title":"rename_property"}]}}}},"401":{"$ref":"#/components/responses/401"},"402":{"$ref":"#/components/responses/402"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/glance":{"get":{"tags":["Projects and overview"],"operationId":"get_glance","summary":"Everything at a glance (menu bar and widgets)","description":"Small enough to poll every minute. `live` is null when Google is unreachable.\n\n**Who:** Everyone in the workspace; a client only sees their own projects.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"projects":{"type":"array","items":{"type":"object"},"description":"id, name, monitored, down, uptime_30d, live, visitors_today, visitors_yesterday, leads_today, open_advice, open_questions"},"totals":{"type":"object","description":"sites_total, sites_up, uptime_30d, visitors_today, visitors_yesterday, leads_today, open_advice, last_deploy, approvals"},"live":{"type":["array","null"],"items":{"type":"object"},"description":"project, live"},"live_total":{"type":["integer","null"]},"down":{"type":"array","items":{"type":"object"},"description":"project, url, since"},"open_questions":{"type":["integer","null"]},"new_advice":{"type":"integer"},"proposals":{"type":"array","items":{"type":"object"}},"studio":{"type":"string","format":"uri"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/extension/connect":{"post":{"tags":["Projects and overview"],"operationId":"post_extension_connect","summary":"Create a key for the browser extension","description":"A key with scope `extension`: it may only use `/api/extension/*`. Studio hands it to the extension through the page; it then appears under API keys, where it can be revoked.\n\n**Who:** The signed-in user.\n\n**Access:** App session only, not with an API key","security":[{"session":[]}],"x-studio-auth":"session","responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"token":{"type":"string"},"studio":{"type":"string","format":"uri"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/extension/sites":{"get":{"tags":["Projects and overview"],"operationId":"get_extension_sites","summary":"Projects with their domains (extension)","description":"Registrable domains per project whose website you may view. The extension leaves your own visits out there, if you turn that on.\n\n**Who:** Everyone in the workspace; a client only sees their own projects.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"projects":{"type":"array","items":{"type":"object"},"description":"id, name, domains"},"studio":{"type":"string","format":"uri"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/extension/site":{"get":{"tags":["Projects and overview"],"operationId":"get_extension_site","summary":"The project for an address, with figures (extension)","description":"**Who:** Everyone in the workspace; a client only sees their own projects.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"url","in":"query","required":true,"schema":{"type":"string","format":"uri"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"project":{"type":["object","null"],"description":"id, name, link"},"visitors_today":{"type":["integer","null"]},"visitors_yesterday":{"type":["integer","null"]},"uptime":{"type":"object","description":"monitored, down, uptime_30d"},"security_score":{"type":["object","null"],"description":"score, previous"},"open_advice":{"type":["integer","null"]},"last_deploy":{"type":["object","null"],"description":"title, at"},"refs_today":{"type":"array","items":{"type":"object"},"description":"ref, visitors"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/extension/post":{"get":{"tags":["Projects and overview"],"operationId":"get_extension_post","summary":"Who follows a post (extension)","description":"**Who:** Everyone in the workspace; a client only sees their own projects.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"url","in":"query","required":true,"schema":{"type":"string","format":"uri"},"description":"Link to a post on Reddit, Hacker News, Bluesky or Mastodon."}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"platform":{"type":"string"},"followed":{"type":"array","items":{"type":"object"},"description":"id, project (id, name), ref, score, comments, last_checked_at, last_error"},"projects":{"type":"array","items":{"type":"object"},"description":"id, name; waar je hem kunt volgen"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Projects and overview"],"operationId":"post_extension_post","summary":"Send the figures of a post you are viewing to Studio (extension)","description":"For Reddit, which refuses servers: the extension reads score and comments from the page you have open yourself, after a click.\n\n**Who:** Anyone who may manage the project website.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"snapshot"},"url":{"type":"string","format":"uri"},"score":{"type":"integer"},"comments":{"type":"integer"},"title":{"type":"string"},"author":{"type":"string"}},"required":["action","url","score","comments"],"title":"snapshot","description":"404 when nobody in this workspace follows the post."},{"type":"object","properties":{"action":{"const":"follow"},"url":{"type":"string","format":"uri"},"project_id":{"type":"integer"},"ref":{"type":"string"},"score":{"type":"integer"},"comments":{"type":"integer"},"title":{"type":"string"},"author":{"type":"string"}},"required":["action","url","project_id"],"title":"follow"}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true},"updated":{"type":"integer"}},"title":"snapshot"},{"type":"object","properties":{"ok":{"const":true},"id":{"type":"integer"},"updated":{"type":"integer"}},"title":"follow"}]}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/runtime":{"get":{"tags":["Projects and overview"],"operationId":"get_runtime","summary":"Versions and vulnerabilities across all projects","description":"PHP, web server, database, operating system and CMS per project (21-10-2026), best source first: the site itself (plugin, module, package), then headers, then Plesk. With the status from endoflife.date: ok, update (a newer release exists in the same line), eol (no more security updates) or onbekend. With `cve` or `product` + `below` you see which projects a vulnerability affects; `certain` is false when only 8.3 is known (Plesk).\n\n**Who:** Team only (owner, member or viewer); clients get 403.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"cve","in":"query","required":false,"schema":{"type":"string"},"description":"A CVE, e.g. CVE-2026-1234: vulnerabilities Studio already found plus the affected versions from the NVD."},{"name":"product","in":"query","required":false,"schema":{"type":"string","enum":["php","nginx","apache","litespeed","mysql","mariadb","postgresql","wordpress","drupal"]}},{"name":"below","in":"query","required":false,"schema":{"type":"string"}},{"name":"from","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"projects":{"type":"array","items":{"type":"object"},"description":"project_id, project, items (per product: label, version, source site|header|plesk, detail, state ok|update|eol|onbekend, cycle, latest, eol), vulnerabilities (serious, total)"},"products":{"type":"object"},"checked_at":{"type":["string","null"]},"lookup":{"type":"object","description":"cve, summary, severity, ranges, projects (project_id, project, product, version, certain, fixed_in, resolved), source nvd|studio|onbekend"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Projects and overview"],"operationId":"post_runtime","summary":"Check versions now","description":"**Who:** Owner or member.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"check"}},"required":["action"],"title":"check","description":"Fetches the releases, takes over what headers and Plesk know and checks plugins and packages for vulnerabilities in the background. At most once a minute."}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true},"projects":{"type":"array","items":{"type":"object"}}},"title":"check"}]}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/tech-overview":{"get":{"tags":["Projects and overview"],"operationId":"get_tech_overview","summary":"Technical overview across all projects","description":"Per project a score with what needs attention: downtime, certificate, domain, vulnerabilities, PHP, Web Vitals, headers, backups and JavaScript errors. See the Home Assistant example above.\n\n**Who:** Team only (owner, member or viewer); clients get 403.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"kpis":{"type":"object","description":"projects, attention, down, certs_14d, domains_30d, vulns_critical_high, vulns_open, php_outdated, vitals_poor, js_new_7d, sources_error, sources_waiting, app_downloads_total (?int, eerste downloads van alle gekoppelde apps sinds de lancering), app_downloads_7d (?int)"},"sources":{"type":"array","items":{"type":"object"},"description":"bronnen met status error, stale of unknown, met project_name (zie GET /api/inventory)"},"projects":{"type":"array","items":{"type":"object"},"description":"id, name, score, status, issues, sites, down, uptime_30d, ssl, domain, vulns, php, php_outdated, vitals, headers, js_errors_7d, js_new_7d, backup, last_deploy"},"apps":{"type":"array","items":{"type":"object"},"description":"project_id, project, connect, downloads_total, downloads_7d, downloads_30d, downloads_prev_30d, crashes_7d, rating, reviews, version"}}},"example":{"ok":true,"kpis":{"projects":9,"attention":2,"down":0,"certs_14d":1,"domains_30d":0,"vulns_critical_high":1,"vulns_open":3,"php_outdated":0,"vitals_poor":0,"js_new_7d":4,"sources_error":1,"sources_waiting":2},"projects":[]}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/source-catalog":{"get":{"tags":["Projects and overview"],"operationId":"get_source_catalog","summary":"Source catalogue: recommended and what is needed","description":"For the catalogue under Sources. `recommended`: per kind the projects where Studio saw the service on the site (What runs) without a connection. `capabilities`: per kind the parts with the minimal rights needed. The state per source is in GET /api/inventory.\n\n**Who:** Team only (owner, member or viewer); clients get 403.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"recommended":{"type":"object","description":"soort → object[]: project_id, project, host, label"},"capabilities":{"type":"object","description":"soort → object[]: key, label, needs, why, write"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/app-downloads":{"get":{"tags":["Projects and overview"],"operationId":"get_app_downloads","summary":"Downloads of a project’s app","description":"From the App Store Connect sales reports that Studio keeps, completed with App Analytics: first-time downloads per day (365 days), per month (24) and per year, the total since launch, countries and the comparison with last year. For a day covered by App Analytics that value counts, as in App Store Connect; `analytics_differs` signals that the sales report says otherwise (often a missing vendor number). `acquisition` is the App Store Connect overview by date: impressions (including product pages), product page views, first-time downloads, redownloads, updates, conversion (downloads ÷ unique impressions), sources, and sessions and crashes (opt-in only), for the last day, 7 and 30 days. Redownloads and updates are separate. `null` means unknown, not zero. Requires the Product module.\n\n**Who:** Everyone in the workspace; a client only sees their own projects.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"project_id","in":"query","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"apps":{"type":"array","items":{"type":"object"},"description":"account_id, app_id, name, history_running, analytics_differs, acquisition (last_day, periods (dag, 7d, 30d: impressions, impressions_unique, page_views, page_views_unique, first_downloads, redownloads, updates, sessions, crashes, installs, deletions, conversion, sources, territories, crashes_by_version, from, to), days, covered) of null, total (downloads, redownloads, updates, complete), d7, d30, prev30, days (day, value), months (month, value, running), years (year, value, running), countries, countries_30d, compare (label, previous_label, value, previous), history (pending, fetched)"}}},"example":{"ok":true,"apps":[{"app_id":"6818309309","name":"Kenteken Spel","total":{"downloads":412,"redownloads":37,"updates":220,"complete":true},"d7":58,"d30":190,"prev30":164}]}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/app-downloads/total":{"get":{"tags":["Projects and overview"],"operationId":"get_app_downloads_total","summary":"Downloads of all apps combined","description":"The top level: all apps of all Apple accounts in the workspace, including apps not linked to a project.\n\n**Who:** Team only (owner, member or viewer); clients get 403.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"include_archived","in":"query","required":false,"schema":{"type":"boolean"},"description":"Also count archived apps; by default per the workspace setting."}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"apps":{"type":"integer"},"total":{"type":["integer","null"]},"d7":{"type":["integer","null"]},"d30":{"type":["integer","null"]},"prev30":{"type":["integer","null"]},"archived_excluded":{"type":"integer"},"include_archived":{"type":"boolean"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/project-overview":{"get":{"tags":["Projects and overview"],"operationId":"get_project_overview","summary":"Overview of one project: what needs attention and the key figures for its type","description":"At most six key figures, picked by project type (webshop, SaaS, local, B2B, content); with an app the last two become downloads and rating. A figure without a source has `missing: true` and `connect` naming what to link. What the viewer may not see per module is left out; revenue and ROAS only for the team or a client who may approve the overview. Downloads are a count and follow the Product module. Without a chosen type, `suggested` holds a proposal; Studio never saves it by itself.\n\n**Who:** Everyone in the workspace; a client only sees their own projects.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"project_id","in":"query","required":true,"schema":{"type":"integer"}},{"name":"days","in":"query","required":false,"schema":{"type":"integer","enum":[7,28,90]},"description":"Defaults to 28."}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"type":{"type":["string","null"]},"suggested":{"type":["object","null"],"description":"type, reason"},"has_app":{"type":"boolean"},"days":{"type":"integer"},"types":{"type":"object"},"can_manage":{"type":"boolean"},"attention":{"type":"object","description":"items (key, tone, text, tab), actions (id, title), checked_at"},"kpis":{"type":"array","items":{"type":"object"},"description":"key, label, value, previous, unit (count|pct|eur|ratio|stars|text), series, tone, hint, invert, of missing en connect (label, kind, tab of href)"},"trend":{"type":"object","description":"visitors, leads, clicks — elk null of een reeks (day, value) met elke dag van de periode"},"funnel":{"type":["array","null"],"items":{"type":"object"},"description":"label, value — bezoeken, formulier begonnen en verstuurd, leads; alleen stappen met gegevens"}}},"example":{"ok":true,"type":"saas","suggested":null,"has_app":true,"days":28,"attention":{"items":[{"key":"ssl","tone":"warn","text":"Certificaat van voorbeeld.nl verloopt over 12 dagen","tab":"uptime"}],"actions":[{"id":41,"title":"Titels herschrijven voor zoektermen op plek 4 tot 10"}],"checked_at":"2026-10-06 10:41:00"},"kpis":[{"key":"status","label":"Bereikbaar (30 d)","value":99.98,"unit":"pct","hint":"nu bereikbaar"},{"key":"signups","label":"Aanmeldingen","value":42,"previous":35,"unit":"count","series":[1,3,2]},{"key":"downloads","label":"Downloads","missing":true,"connect":{"label":"Koppel App Store Connect","kind":"appstore_connect"}}]}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/work":{"get":{"tags":["Projects and overview"],"operationId":"get_work","summary":"Work board: recommendations across clients and workload","description":"**Who:** Team only (owner, member or viewer); clients get 403.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"assignee","in":"query","required":false,"schema":{"type":"string"},"description":"`me` or a user id."}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"columns":{"type":"object","description":"open, planned, done"},"workload":{"type":"array","items":{"type":"object"},"description":"project_id, name, open, planned, overdue, done_30"},"team":{"type":"array","items":{"type":"object"}},"me":{"type":"integer"},"today":{"type":"string","format":"date"},"health":{"type":"array","items":{"type":"object"}}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/portfolio":{"get":{"tags":["Projects and overview"],"operationId":"get_portfolio","summary":"Project list: one row per project","description":"The team start page: per project status (ok, warn, bad) with what needs attention, uptime, tags, clients, visitors and the result that fits the project type, the app and the latest report. The same figures as Techniek and the project overview.\n\n**Who:** Team only (owner, member or viewer); clients get 403.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","enum":[7,28,90]},"description":"Defaults to 28."}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"projects":{"type":"array","items":{"type":"object"},"description":"id, name, color, template, suggested, tags, clients (id, name), status, issues, uptime_30d, down, headline (visitors, result: key, label, value, previous, unit), app (downloads_30d, downloads_prev_30d, downloads_total, rating, reviews), report (period, status), open_advice"},"kpis":{"type":"object","description":"projects, attention, visitors, downloads_7d, open_advice"},"unassigned_sources":{"type":"integer"},"templates":{"type":"object"},"days":{"type":"integer"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/portfolio-trend":{"get":{"tags":["Projects and overview"],"operationId":"get_portfolio_trend","summary":"Trend across all projects","description":"Visitors, conversions (GA4 key events plus leads from the site script) and revenue (paid minus refunded, plus shop revenue) per day across all projects, with the total, the previous period and the split per project. A metric without any source is null.\n\n**Who:** Team only (owner, member or viewer); clients get 403.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","enum":[7,28,90]},"description":"Defaults to 28."}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"days":{"type":"integer"},"metrics":{"type":"object","description":"visitors, conversions, revenue — elk null of { unit, total, previous, series (day, value), by_project (id, name, color, value) }"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/benchmarks":{"get":{"tags":["Projects and overview"],"operationId":"get_benchmarks","summary":"Benchmarks: a project next to similar projects","description":"Median, 25th and 75th percentile and the number of projects per metric, plus this project's value and position (`better_than` of `others`). Computed nightly. Figures only with at least 5 projects in the group (`enough`); shared across workspaces also from at least 3 workspaces, and only when your own workspace shares. Never names or values of other projects. Without `project_id`: per metric across all projects of the workspace, with the projects in the weakest quarter (team only). A client gets `hidden: true` unless the team turns benchmarks on for clients; clients never see custom tags or shared figures.\n\n**Who:** Team; clients with access to the Overview module.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"project_id","in":"query","required":false,"schema":{"type":"integer"}},{"name":"period","in":"query","required":false,"schema":{"type":"integer","enum":[28,90]},"description":"Defaults to 28."},{"name":"scope","in":"query","required":false,"schema":{"type":"string","enum":["werkruimte","gedeeld"]},"description":"Defaults to werkruimte."},{"name":"group","in":"query","required":false,"schema":{"type":"string"},"description":"Key from `groups`, e.g. `soort:webshop` or `tech:drupal`. Without: the most specific group with the most usable metrics."}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"period":{"type":"integer"},"scope":{"type":"string"},"group":{"type":"string"},"groups":{"type":"array","items":{"type":"object"},"description":"key, label, usable"},"metrics":{"type":"array","items":{"type":"object"},"description":"metric, label, unit, higher_is_better, note, value, n, min, enough, median, p25, p75, position (better_than, others, percentile); zonder project_id: behind (project_id, name, value) in plaats van value en position"},"settings":{"type":"object","description":"Only without project_id: share, show_clients."},"computed_at":{"type":["string","null"]},"hidden":{"type":"boolean"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Projects and overview"],"operationId":"post_benchmarks","summary":"Configure benchmarks","description":"**Who:** Owner or member; viewers and clients get 403.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"settings"},"share":{"type":"boolean","description":"Take part anonymously in the comparison across workspaces. Off by default."},"show_clients":{"type":"boolean","description":"Clients see their own project's position. Off by default."}},"required":["action"],"title":"settings"}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true},"settings":{"type":"object","description":"share, show_clients"}},"title":"settings"}]}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/report-overview":{"get":{"tags":["Projects and overview"],"operationId":"get_report_overview","summary":"Monthly reports across all projects","description":"Per project the state of one month's report: missing, draft (fetch errors), ready, sent or opened (by a client, in Studio or via a share link; only a count, not who).\n\n**Who:** Team only (owner, member or viewer); clients get 403.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"period","in":"query","required":false,"schema":{"type":"string"},"description":"A closed month like 2026-09; defaults to last month."}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"period":{"type":"string"},"label":{"type":"string"},"periods":{"type":"array","items":{"type":"object"},"description":"period, label"},"rows":{"type":"array","items":{"type":"object"},"description":"project_id, project, color, clients, tags, report_id, status, errors, generated_at, published_at, views, first_viewed_at"},"counts":{"type":"object","description":"ontbreekt, concept, klaar, verstuurd, geopend"},"can_manage":{"type":"boolean"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Projects and overview"],"operationId":"post_report_overview","summary":"Create drafts and send ready reports","description":"**Who:** Owner or member; viewers and clients get 403.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"generate_missing"},"period":{"type":"string"}},"required":["action"],"title":"generate_missing","description":"A draft for every project without a report, in the background (202)."},{"type":"object","properties":{"action":{"const":"publish_ready"},"period":{"type":"string"},"ids":{"type":"array","items":{"type":"integer"},"description":"Only these reports; defaults to all that are ready."}},"required":["action"],"title":"publish_ready","description":"Only ready reports; a draft with errors stays. Clients get the report mail through the default automation."}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true},"queued":{"type":"integer"},"tasks":{"type":"array","items":{"type":"string"}}},"title":"generate_missing"},{"type":"object","properties":{"ok":{"const":true},"published":{"type":"integer"},"notified":{"type":"integer"}},"title":"publish_ready"}]}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/today":{"get":{"tags":["Projects and overview"],"operationId":"get_today","summary":"Today: what needs attention across all projects","description":"Outages, critical and high vulnerabilities (per package and version across all projects), certificates and domains expiring within 14 days, sources that fail, new errors after a deploy, a drop in the App Store rating, actions waiting for you and last month's reports still in draft. Each item has a `to` pointing to where you fix it.\n\n**Who:** Team only (owner, member or viewer); clients get 403.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"items":{"type":"array","items":{"type":"object"},"description":"key, group (storing|lek|verloop|bron|fout|beoordeling|actie|rapport), tone (bad|warn|info), title, detail, projects (id, name), to, fix (?object: steps[], links[] (label, url), copy[] (label, value); \"Zo los je het op\")"},"groups":{"type":"object"},"counts":{"type":"object","description":"bad, total"}}},"example":{"ok":true,"items":[{"key":"lek-0","group":"lek","tone":"bad","title":"Drupal core 10.2.3, 3 projecten","detail":"2 lekken, ernst kritiek · opgelost in 10.2.4","projects":[{"id":3,"name":"Alpha"}],"to":"/techniek"}],"counts":{"bad":1,"total":1}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/tags":{"get":{"tags":["Projects and overview"],"operationId":"get_tags","summary":"Project tags","description":"Platform (Web, iOS) and technology (Drupal 11, Laravel 13, PHP 8.4) are detected by Studio from integrations, lockfiles, the Drupal module or WordPress plugin, the site check and Plesk; `origin` and `origin_at` say where and when. Own tags are made by the team.\n\n**Who:** Team only (owner, member or viewer); clients get 403.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"project_id","in":"query","required":false,"schema":{"type":"integer"},"description":"Only this project."},{"name":"hidden","in":"query","required":false,"schema":{"type":"boolean"},"description":"Also switched-off automatic tags."}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"tags":{"type":"array","items":{"type":"object"},"description":"id, project_id, kind (platform|tech|own), slug, key, label, color, auto, hidden, origin, origin_at"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Projects and overview"],"operationId":"post_tags","summary":"Add, switch off and re-detect tags","description":"**Who:** Owner or member; viewers and clients get 403.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"add"},"project_id":{"type":"integer"},"label":{"type":"string","description":"At most 40 characters."},"color":{"type":"string","description":"As #rrggbb."}},"required":["action","project_id","label"],"title":"add"},{"type":"object","properties":{"action":{"const":"remove"},"tag_id":{"type":"integer"}},"required":["action","tag_id"],"title":"remove","description":"An own tag is deleted; an automatic tag is switched off and does not come back on the next detection."},{"type":"object","properties":{"action":{"const":"restore"},"tag_id":{"type":"integer"}},"required":["action","tag_id"],"title":"restore"},{"type":"object","properties":{"action":{"const":"detect"},"project_id":{"type":"integer","description":"Without: all projects."}},"required":["action"],"title":"detect"}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true},"tag":{"type":"object"}},"title":"add"},{"type":"object","properties":{"ok":{"const":true}},"title":"remove"},{"type":"object","properties":{"ok":{"const":true},"tag":{"type":"object"}},"title":"restore"},{"type":"object","properties":{"ok":{"const":true},"tags":{"type":"array","items":{"type":"object"}}},"title":"detect"}]}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/views":{"get":{"tags":["Projects and overview"],"operationId":"get_views","summary":"Your saved views of the project list","description":"**Who:** Team only (owner, member or viewer); clients get 403.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"views":{"type":"array","items":{"type":"object"},"description":"id, name, filters (client, type, tags, attention)"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Projects and overview"],"operationId":"post_views","summary":"Save and delete views","description":"**Who:** Team only (owner, member or viewer); clients get 403.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"save"},"name":{"type":"string"},"filters":{"type":"object","description":"client (?int), type (?string), tags (string[], as kind:slug, e.g. tech:drupal), attention (bool)."},"view_id":{"type":"integer","description":"With id: update."}},"required":["action","name"],"title":"save"},{"type":"object","properties":{"action":{"const":"delete"},"view_id":{"type":"integer"}},"required":["action","view_id"],"title":"delete"}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true},"view_id":{"type":"integer"}},"title":"save"},{"type":"object","properties":{"ok":{"const":true}},"title":"delete"}]}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/preferences":{"get":{"tags":["Projects and overview"],"operationId":"get_preferences","summary":"Your personal preferences","description":"**Who:** Team only (owner, member or viewer); clients get 403.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"start_page":{"type":["string","null"],"description":"je eigen keuze, null is de standaard"},"start_page_default":{"type":"string"},"options":{"type":"object"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Projects and overview"],"operationId":"post_preferences","summary":"Choose your start page, mark What's new as read, app language","description":"**Who:** Everyone for `locale`; the rest team only.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"start_page"},"start_page":{"type":["string","null"],"enum":["overzicht","vandaag","projecten","techniek",null]}},"required":["action"],"title":"start_page","description":"Null restores the default: Vandaag for a team of one, Projecten for a team."},{"type":"object","properties":{"action":{"const":"changelog_seen"},"version":{"type":"string","description":"Date of the newest version, yyyy-mm-dd."}},"required":["action","version"],"title":"changelog_seen","description":"Never moves back in time; /api/me returns changelog_seen."},{"type":"object","properties":{"action":{"const":"locale"},"locale":{"type":"string","enum":["nl","en"]}},"required":["action","locale"],"title":"locale","description":"App language for this user; /api/me returns it as user.locale. Defaults to nl."},{"type":"object","properties":{"action":{"const":"project_order"},"ids":{"type":"array","items":{"type":"integer"},"description":"Project ids in the desired order."}},"required":["action","ids"],"title":"project_order","description":"Per user; clients too. Projects you can't see are skipped. GET /api/projects and /api/portfolio sort by it."}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true},"start_page":{"type":"string"}},"title":"start_page"},{"type":"object","properties":{"ok":{"const":true},"changelog_seen":{"type":"string"}},"title":"changelog_seen"},{"type":"object","properties":{"ok":{"const":true},"locale":{"type":"string"}},"title":"locale"},{"type":"object","properties":{"ok":{"const":true},"ids":{"type":"array","items":{"type":"integer"}}},"title":"project_order"}]}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/portal":{"get":{"tags":["Projects and overview"],"operationId":"get_portal","summary":"Client start page","description":"Per project this month's results against the same days last month, goals, what was done and what's next.\n\n**Who:** Everyone in the workspace; a client only sees their own projects.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"month":{"type":"string"},"days_so_far":{"type":"integer"},"projects":{"type":"array","items":{"type":"object"},"description":"id, name, numbers, series (day, value: bezoeken per dag deze maand), goals, done, next, report, weekly_update"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Projects and overview"],"operationId":"post_portal","summary":"Turn the weekly update on or off","description":"**Who:** Clients with access to the project.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"weekly"},"project_id":{"type":"integer"},"on":{"type":"boolean"}},"required":["action","project_id","on"],"title":"weekly"}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true},"weekly_update":{"type":"boolean"}},"title":"weekly"}]}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/notifications":{"get":{"tags":["Projects and overview"],"operationId":"get_notifications","summary":"Notification centre: what happened","description":"The bell in the app, per user and workspace, for the last 30 days: outages and recoveries, anomalies, newly found sources, approvals, client questions, rejected app versions, confirmed advice and new form submissions. A client only sees outages of their own projects, answers to their questions and submissions when allowed to view Website. Outages and anomalies follow the account's notification preferences.\n\n**Who:** Everyone in the workspace; a client only sees their own projects.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"count","in":"query","required":false,"schema":{"type":"boolean"},"description":"Only the unread count."}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"items":{"type":"array","items":{"type":"object"},"description":"key, kind (storing|afwijking|nieuw|goedkeuring|vraag|antwoord|app|bevestigd|inzending), tone (bad|warn|info|ok), title, detail, at (?datetime), to, project (?object: id, name), read"},"unread":{"type":"integer"},"kinds":{"type":"object"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Projects and overview"],"operationId":"post_notifications","summary":"Mark notifications as read","description":"**Who:** Everyone in the workspace; a client only sees their own projects.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"read"},"keys":{"type":"array","items":{"type":"string"},"description":"Keys from items[].key, at most 200."}},"required":["action","keys"],"title":"read"},{"type":"object","properties":{"action":{"const":"read_all"}},"required":["action"],"title":"read_all","description":"Everything currently in the bell; anything after that is new again."}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true},"unread":{"type":"integer"}},"title":"read"},{"type":"object","properties":{"ok":{"const":true},"marked":{"type":"integer"},"unread":{"type":"integer"}},"title":"read_all"}]}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/start-checklist":{"get":{"tags":["Projects and overview"],"operationId":"get_start_checklist","summary":"Start checklist for a workspace","description":"The steps that get a workspace going, computed from the current state: Google, first project, type, uptime, script, Search Console, client, and for the owner two-factor and billing details.\n\n**Who:** Owner and team members; null for everyone else.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"checklist":{"type":["object","null"],"description":"steps (object[]: key, done, title, why, to, action), done (int), total (int), complete (bool), hidden (bool)"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Projects and overview"],"operationId":"post_start_checklist","summary":"Hide or show the start checklist","description":"**Who:** Owner and team members.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"hide"}},"required":["action"],"title":"hide","description":"Per person and per workspace."},{"type":"object","properties":{"action":{"const":"show"}},"required":["action"],"title":"show"}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true},"checklist":{"type":"object"}},"title":"hide"},{"type":"object","properties":{"ok":{"const":true},"checklist":{"type":"object"}},"title":"show"}]}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/search":{"get":{"tags":["Projects and overview"],"operationId":"get_search","summary":"Search projects, recommendations, queries and pages","description":"**Who:** Everyone in the workspace; a client only sees their own projects.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"q","in":"query","required":true,"schema":{"type":"string"},"description":"At least two characters."}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"results":{"type":"array","items":{"type":"object"},"description":"kind, title, subtitle, project_id, tab, filter"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/task":{"get":{"tags":["Projects and overview"],"operationId":"get_task","summary":"Status of a background task","description":"**Who:** Everyone in the workspace; a client only sees their own projects.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"id","in":"query","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"status":{"type":"string","enum":["queued","running","done","failed"]},"result":{"type":["object","null"]},"error":{"type":["string","null"]}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/inventory":{"get":{"tags":["Projects and overview"],"operationId":"get_inventory","summary":"All sources with their status","description":"GA4, Search Console, own systems, extra services and uptime checks, with whether each one works.\n\n**Who:** Team only (owner, member or viewer); clients get 403.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"project_id","in":"query","required":false,"schema":{"type":"integer"},"description":"Only this project's sources; then without `projects`."}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"items":{"type":"array","items":{"type":"object"},"description":"key, type, subtype, id, name, detail, project_id, status (ok|error|stale|unknown|off|setup|tracking), status_text, action, fix (?object: steps[], links[] (label, url), copy[] (label, value); \"Zo los je het op\"), last_ok_at, last_error_at, fetching, can_fetch, can_assign, missing (object[]: id, label, since, since_text; cijfers die de bron eerder meestuurde en al een tijd niet meer)"},"projects":{"type":"array","items":{"type":"object"},"description":"id, name, color, sources, problems, unchecked, clients, open_advice, monitors_down, latest_report"},"counts":{"type":"object","description":"error, waiting, ok"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Projects and overview"],"operationId":"post_inventory","summary":"Fetch a source now, or stop expecting a missing figure","description":"**Who:** Owner or member; viewers and clients get 403.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"fetch"},"type":{"type":"string","enum":["ga","search","app","integration"]},"id":{"type":"integer"}},"required":["action","type","id"],"title":"fetch","description":"Single-request sources (most integrations, own systems) right away; GA4, Search Console, payments, Ads and App Store Connect through the queue, with `fetching: true` until done. At most once a minute per source (429)."},{"type":"object","properties":{"action":{"const":"ignore_metric"},"id":{"type":"integer","description":"The `id` from `missing`."}},"required":["action","id"],"title":"ignore_metric","description":"The figure was removed on purpose: no more signal. If it comes back later, it counts again."}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true},"item":{"type":"object","description":"zoals in GET /api/inventory"}},"title":"fetch"},{"type":"object","properties":{"ok":{"const":true},"ignored":{"type":"boolean"}},"title":"ignore_metric"}]}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/discovery":{"get":{"tags":["Projects and overview"],"operationId":"get_discovery","summary":"Newly found sources","description":"What Studio sees through a connection but is not linked anywhere (Analytics properties, Search Console sites, apps, business profiles and, for admins, Plesk domains), plus what was ignored or archived. Studio never decides on its own. Types set to \"hide\" only count in `hidden`.\n\n**Who:** Team only (owner, member or viewer); clients get 403.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"new":{"type":"array","items":{"type":"object"},"description":"key, type (ga|search|apple|gbp|plesk), ref, name, detail, project_id, project_name, state, decided_at, data_deleted, suggestion {name, template}"},"ignored":{"type":"array","items":{"type":"object"}},"archived":{"type":"array","items":{"type":"object"}},"linked":{"type":"array","items":{"type":"object"}},"archived_projects":{"type":"array","items":{"type":"object"},"description":"id, name, archived_at"},"hidden":{"type":"integer"},"notify":{"type":"integer"},"settings":{"type":"object","description":"modes {ga, search, apple, gbp, plesk: melden|stil|verbergen}, include_archived"},"can_manage":{"type":"boolean"},"projects":{"type":"array","items":{"type":"object"},"description":"id, name"},"templates":{"type":"array","items":{"type":"object"},"description":"key, label"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Projects and overview"],"operationId":"post_discovery","summary":"Link, ignore, archive or restore","description":"**Who:** Owner or member; viewers and clients get 403.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"link"},"key":{"type":"string"},"project_id":{"type":"integer"}},"required":["action","key","project_id"],"title":"link"},{"type":"object","properties":{"action":{"const":"new_project"},"key":{"type":"string"},"name":{"type":"string"},"template":{"type":"string"}},"required":["action","key","name","template"],"title":"new_project"},{"type":"object","properties":{"action":{"const":"ignore"},"keys":{"type":"array","items":{"type":"string"}}},"required":["action","keys"],"title":"ignore"},{"type":"object","properties":{"action":{"const":"archive"},"keys":{"type":"array","items":{"type":"string"}},"delete_data":{"type":"boolean","description":"Also delete the fetched data; kept by default."}},"required":["action","keys"],"title":"archive","description":"Studio then fetches nothing and leaves the source out of totals. A linked Analytics or Search Console source is detached from its project (remembered for restoring); a linked app or location stays attached."},{"type":"object","properties":{"action":{"const":"restore"},"keys":{"type":"array","items":{"type":"string"}}},"required":["action","keys"],"title":"restore"},{"type":"object","properties":{"action":{"const":"settings"},"modes":{"type":"object"},"include_archived":{"type":"boolean"}},"required":["action"],"title":"settings"},{"type":"object","properties":{"action":{"const":"search_locations"}},"required":["action"],"title":"search_locations"}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true}},"title":"link"},{"type":"object","properties":{"ok":{"const":true},"project_id":{"type":"integer"}},"title":"new_project"},{"type":"object","properties":{"ok":{"const":true},"count":{"type":"integer"}},"title":"ignore"},{"type":"object","properties":{"ok":{"const":true},"count":{"type":"integer"}},"title":"archive"},{"type":"object","properties":{"ok":{"const":true},"count":{"type":"integer"}},"title":"restore"},{"type":"object","properties":{"ok":{"const":true},"settings":{"type":"object"}},"title":"settings"},{"type":"object","properties":{"ok":{"const":true},"found":{"type":"integer"}},"title":"search_locations"}]}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/capabilities":{"get":{"tags":["Projects and overview"],"operationId":"get_capabilities","summary":"What Studio can do with a connection","description":"Per capability what is minimally needed, what the key may do according to the last measurement (yes, no, unknown) and whether it is on. Off means Studio does not fetch it at all. `minimal` says which rights suffice for what is on; `warnings` lists keys with more rights than needed.\n\n**Who:** Team only (owner, member or viewer); clients get 403.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"subject_type","in":"query","required":true,"schema":{"type":"string"},"description":"apple, google, integration, slack or chat."},{"name":"subject_id","in":"query","required":true,"schema":{"type":"integer"},"description":"Id of the Apple account, Google connection, project integration, workspace (slack) or channel."}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"type":{"type":"string"},"name":{"type":"string"},"items":{"type":"array","items":{"type":"object"},"description":"cap, label, needs, why, write, enabled, have (ja|nee|onbekend), detail, state (aan|uit|niet mogelijk|niet gemeten)"},"measured_at":{"type":["string","null"]},"warnings":{"type":"array","items":{"type":"string"}},"fix":{"type":["object","null"],"description":"Where to lower the rights: steps, links (label, url), copy (label, value)."},"keys":{"type":"array","items":{"type":"object"},"description":"key_id, role, role_label, account_key, allowed (alleen Apple)"},"minimal":{"type":["string","null"]},"can_manage":{"type":"boolean"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Projects and overview"],"operationId":"post_capabilities","summary":"Turn a capability on or off, or measure again","description":"**Who:** Owner or member; viewers and clients get 403.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"toggle"},"subject_type":{"type":"string"},"subject_id":{"type":"integer"},"cap":{"type":"string"},"enabled":{"type":"boolean"}},"required":["action","subject_type","subject_id","cap","enabled"],"title":"toggle"},{"type":"object","properties":{"action":{"const":"measure"},"subject_type":{"type":"string"},"subject_id":{"type":"integer"}},"required":["action","subject_type","subject_id"],"title":"measure","description":"Read-only requests to the service; a capability that would change something is never tried. At most once a minute (429)."}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true},"items":{"type":"array","items":{"type":"object"}}},"title":"toggle"},{"type":"object","properties":{"ok":{"const":true},"items":{"type":"array","items":{"type":"object"}}},"title":"measure"}]}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/dashboards":{"get":{"tags":["Projects and overview"],"operationId":"get_dashboards","summary":"All dashboards of the workspace","description":"Dashboards across projects (without `project_id`) first, then the per-project ones.\n\n**Who:** Team only (owner, member or viewer); clients get 403.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"dashboards":{"type":"array","items":{"type":"object"},"description":"id, name, goal, project_id, project_name, tiles (aantal), scope"},"can_edit":{"type":"boolean"},"limit":{"type":["integer","null"]},"workspace_count":{"type":"integer"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/dashboard":{"get":{"tags":["Projects and overview"],"operationId":"get_dashboard","summary":"A project dashboard, or one across projects","description":"With `project_id` a project dashboard. Only `dashboard_id` without `project_id` returns a dashboard across projects (team only): `tiles` over the projects in `scope`, plus single-project tiles; `catalog` and `project_catalog` are the choices.\n\n**Who:** Team; clients with access to the Overview module.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"project_id","in":"query","required":false,"schema":{"type":"integer"},"description":"Empty for a dashboard across projects."},{"name":"dashboard_id","in":"query","required":false,"schema":{"type":"integer"},"description":"Defaults to the first."}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"dashboards":{"type":"array","items":{"type":"object"},"description":"id, name, goal"},"current":{"type":"object","description":"id, name, goal"},"tiles":{"type":"array","items":{"type":"object"}},"chosen":{"type":"array","items":{"type":"string"}},"catalog":{"type":"object"},"goals":{"type":"object"},"can_edit":{"type":"boolean"},"limit":{"type":["integer","null"]}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Projects and overview"],"operationId":"post_dashboard","summary":"Create, edit and delete dashboards","description":"**Who:** Owner or member; viewers and clients get 403.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"create"},"project_id":{"type":"integer","description":"Empty: a dashboard across projects."},"goal":{"type":"string","description":"A goal from `goals`; defaults to custom."},"name":{"type":"string"}},"required":["action"],"title":"create"},{"type":"object","properties":{"action":{"const":"save"},"dashboard_id":{"type":"integer"},"tiles":{"type":["object","string"],"description":"At most twelve, from `catalog`; across projects also {key, project_id} for one project."},"name":{"type":"string"},"scope":{"type":"object","description":"Across projects only: {tags: [\"platform:ios\"], project_ids: [3]}; empty means all projects."}},"required":["action","dashboard_id"],"title":"save"},{"type":"object","properties":{"action":{"const":"delete"},"dashboard_id":{"type":"integer"}},"required":["action","dashboard_id"],"title":"delete","description":"A project keeps its last dashboard."}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true},"id":{"type":"integer"}},"title":"create"},{"type":"object","properties":{"ok":{"const":true}},"title":"save"},{"type":"object","properties":{"ok":{"const":true}},"title":"delete"}]}}}},"401":{"$ref":"#/components/responses/401"},"402":{"$ref":"#/components/responses/402"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/goals":{"get":{"tags":["Projects and overview"],"operationId":"get_goals","summary":"Monthly goals, forecast and benchmark","description":"**Who:** Team; clients with access to the Overview module.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"project_id","in":"query","required":true,"schema":{"type":"integer"},"description":"Project id."}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"goals":{"type":"array","items":{"type":"object"},"description":"metric, label, target, value, projected, status"},"metrics":{"type":"object"},"can_manage":{"type":"boolean"},"suggested":{"type":["object","null"]},"forecast":{"type":["object","null"]},"languages":{"type":["object","null"]},"benchmark":{"type":["object","null"]},"signals":{"type":["object","null"]}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Projects and overview"],"operationId":"post_goals","summary":"Set or remove a monthly goal","description":"**Who:** Owner or member (manages Overview); others get 404.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"set"},"project_id":{"type":"integer"},"metric":{"type":"string","enum":["sessions","key_events","new_users","clicks"]},"target":{"type":"integer","description":"At least 1."}},"required":["action","project_id","metric","target"],"title":"set"},{"type":"object","properties":{"action":{"const":"delete"},"project_id":{"type":"integer"},"metric":{"type":"string","enum":["sessions","key_events","new_users","clicks"]}},"required":["action","project_id","metric"],"title":"delete"}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true},"goals":{"type":"array","items":{"type":"object"}}},"title":"set"},{"type":"object","properties":{"ok":{"const":true},"goals":{"type":"array","items":{"type":"object"}}},"title":"delete"}]}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/annotations":{"get":{"tags":["Projects and overview"],"operationId":"get_annotations","summary":"Project timeline","description":"**Who:** Team; clients with access to the Overview module.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"project_id","in":"query","required":true,"schema":{"type":"integer"},"description":"Project id."},{"name":"days","in":"query","required":false,"schema":{"type":"integer"},"description":"1 to 800; defaults to 28."}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"annotations":{"type":"array","items":{"type":"object"},"description":"id, kind, title, detail, url, happened_at"},"can_manage":{"type":"boolean"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Projects and overview"],"operationId":"post_annotations","summary":"Timeline event or deploy key","description":"**Who:** Owner or member; viewers and clients get 403.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"create"},"project_id":{"type":["integer","null"],"description":"Null: for all projects."},"date":{"type":"string","format":"date"},"kind":{"type":"string","enum":["notitie","google","meting","deploy"]},"title":{"type":"string"},"detail":{"type":"string"},"url":{"type":"string","format":"uri"}},"required":["action","title"],"title":"create"},{"type":"object","properties":{"action":{"const":"delete"},"id":{"type":"integer"}},"required":["action","id"],"title":"delete"},{"type":"object","properties":{"action":{"const":"deploy_token"},"project_id":{"type":["integer","null"]}},"required":["action"],"title":"deploy_token"}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true}},"title":"create"},{"type":"object","properties":{"ok":{"const":true}},"title":"delete"},{"type":"object","properties":{"ok":{"const":true},"token":{"type":"string","description":"For POST /api/deploys and /api/components; shown once."},"url":{"type":"string","format":"uri"}},"title":"deploy_token"}]}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/checklist":{"get":{"tags":["Projects and overview"],"operationId":"get_checklist","summary":"Project setup checklist","description":"**Who:** Team only (owner, member or viewer); clients get 403.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"project_id","in":"query","required":true,"schema":{"type":"integer"},"description":"Project id."}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"sections":{"type":"array","items":{"type":"object"}},"can_manage":{"type":"boolean"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Projects and overview"],"operationId":"post_checklist","summary":"Tick off a checklist item or recheck","description":"**Who:** Owner or member; viewers and clients get 403.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"set"},"project_id":{"type":"integer"},"key":{"type":"string"},"state":{"type":["string","null"],"enum":["done","na",null],"description":"Null resets the item."},"note":{"type":"string"}},"required":["action","project_id","key"],"title":"set"},{"type":"object","properties":{"action":{"const":"recheck"},"project_id":{"type":"integer"}},"required":["action","project_id"],"title":"recheck"}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true},"sections":{"type":"array","items":{"type":"object"}}},"title":"set"},{"type":"object","properties":{"ok":{"const":true},"task_id":{"type":"string","description":"Follow the task with GET /api/task?id=…"}},"title":"recheck"}]}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/hours":{"get":{"tags":["Projects and overview"],"operationId":"get_hours","summary":"Hour budget per client","description":"**Who:** Team only (owner, member or viewer); clients get 403.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"month","in":"query","required":false,"schema":{"type":"string","pattern":"^\\d{4}-(0[1-9]|1[0-2])$"},"description":"Defaults to this month."}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"month":{"type":"string","pattern":"^\\d{4}-(0[1-9]|1[0-2])$"},"projects":{"type":"array","items":{"type":"object"},"description":"project_id, name, budget, spent, pace"},"entries":{"type":"array","items":{"type":"object"},"description":"id, project_id, project, day, minutes, note, user, advice"},"can_manage":{"type":"boolean"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Projects and overview"],"operationId":"post_hours","summary":"Log hours and set budgets","description":"**Who:** Team only (owner, member or viewer); clients get 403.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"budget"},"project_id":{"type":"integer"},"hours":{"type":["number","null"],"description":"Per month; empty means no agreement."}},"required":["action","project_id"],"title":"budget","description":"Workspace owner only."},{"type":"object","properties":{"action":{"const":"log"},"project_id":{"type":"integer"},"day":{"type":"string","format":"date","description":"Not in the future."},"minutes":{"type":"integer","description":"1 to 1440."},"note":{"type":"string"},"recommendation_id":{"type":"integer"}},"required":["action","project_id","minutes"],"title":"log"},{"type":"object","properties":{"action":{"const":"delete"},"id":{"type":"integer"}},"required":["action","id"],"title":"delete","description":"Your own hours, or as owner anyone's."}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true}},"title":"budget"},{"type":"object","properties":{"ok":{"const":true},"id":{"type":"integer"}},"title":"log"},{"type":"object","properties":{"ok":{"const":true}},"title":"delete"}]}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/page-insight":{"get":{"tags":["Projects and overview"],"operationId":"get_page_insight","summary":"What Studio knows about one page","description":"For the browser extension: finds the project by domain and returns crawl, queries, recommendations and speed for that path, per module you can access.\n\n**Who:** Everyone in the workspace; a client only sees their own projects.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"url","in":"query","required":true,"schema":{"type":"string","format":"uri"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"project":{"type":["object","null"],"description":"id, name"},"path":{"type":"string"},"crawl":{"type":["object","null"]},"queries":{"type":"array","items":{"type":"object"}},"advice":{"type":"array","items":{"type":"object"}},"speed":{"type":["object","null"]},"link":{"type":"string","format":"uri"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/domains":{"get":{"tags":["Projects and overview"],"operationId":"get_domains","summary":"Domains of a project","description":"**Who:** Owner or member (manages Website); others get 404.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"project_id","in":"query","required":true,"schema":{"type":"integer"},"description":"Project id."}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"domains":{"type":"array","items":{"type":"object"},"description":"id, domain, record, verified, via (dns|plugin), plugin, primary, source (auto|manual), verified_at, checked_at"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Projects and overview"],"operationId":"post_domains","summary":"Verify, add, remove or make a domain the main one","description":"**Who:** Owner or member (manages Website); others get 404.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"check"},"project_id":{"type":"integer"},"domain":{"type":"string"}},"required":["action","project_id","domain"],"title":"check"},{"type":"object","properties":{"action":{"const":"add"},"project_id":{"type":"integer"},"domain":{"type":"string","description":"A domain or address; Studio keeps the domain itself (without www)."}},"required":["action","project_id","domain"],"title":"add"},{"type":"object","properties":{"action":{"const":"remove"},"project_id":{"type":"integer"},"domain":{"type":"string"}},"required":["action","project_id","domain"],"title":"remove","description":"The domain does not come back, not even through an old Search Console property. Site checks on it go too."},{"type":"object","properties":{"action":{"const":"primary"},"project_id":{"type":"integer"},"domain":{"type":"string"},"move":{"type":"boolean","description":"Default true: uptime checks move along and the old main domain is removed."}},"required":["action","project_id","domain"],"title":"primary"}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true},"verified":{"type":"boolean"},"domains":{"type":"array","items":{"type":"object"}}},"title":"check"},{"type":"object","properties":{"ok":{"const":true},"verified":{"type":"boolean"},"domains":{"type":"array","items":{"type":"object"}}},"title":"add"},{"type":"object","properties":{"ok":{"const":true},"left":{"type":"object","description":"monitors (string[]), search_console (string[]); wijst nog naar het domein"},"domains":{"type":"array","items":{"type":"object"}}},"title":"remove"},{"type":"object","properties":{"ok":{"const":true},"website":{"type":"object","description":"moved, search_console"},"domains":{"type":"array","items":{"type":"object"}}},"title":"primary"}]}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/mail-funnels":{"get":{"tags":["Projects and overview"],"operationId":"get_mail_funnels","summary":"Mail funnels from other systems","description":"What a site sends itself (field `mail_funnels` in the metrics contract), later also Mailchimp and Brevo. Counts only. Without `project_id` all projects (team only).\n\n**Who:** The team; with `project_id` also clients with Mail funnels.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"project_id","in":"query","required":false,"schema":{"type":"integer"}},{"name":"days","in":"query","required":false,"schema":{"type":"integer","enum":[7,30,90]}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"days":{"type":"integer"},"funnels":{"type":"array","items":{"type":"object"},"description":"id, key, project_id, project, origin (site|mailchimp|brevo), name, status (actief|aan|uit|weg), trigger, audience, goal, url (alleen team), seen_at, sent, clicked, converted, unsubscribed, delivered, bounced, complained, revenue, click_rate, conversion_rate, unsubscribe_rate, series (date, sent, clicked, converted, unsubscribed), steps (id, name, subject + dezelfde tellers)"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/social-posts":{"get":{"tags":["Projects and overview"],"operationId":"get_social_posts","summary":"Followed social media posts","description":"**Who:** Anyone who may view the project website.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"project_id","in":"query","required":true,"schema":{"type":"integer"},"description":"Project id."}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"posts":{"type":"array","items":{"type":"object"},"description":"id, platform (reddit|hackernews|bluesky|mastodon), url, title, community, author, ref, posted_at, score, comments, ratio, last_checked_at, last_error, series (at, score, comments), visitors, signups, days (day, visitors, signups)"},"can_manage":{"type":"boolean"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Projects and overview"],"operationId":"post_social_posts","summary":"Follow, update or stop following a post","description":"**Who:** Anyone who may manage the project website.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"add"},"project_id":{"type":"integer"},"url":{"type":"string","format":"uri","description":"Link to a post on Reddit, Hacker News, Bluesky or Mastodon."},"ref":{"type":"string"}},"required":["action","project_id","url"],"title":"add"},{"type":"object","properties":{"action":{"const":"update"},"id":{"type":"integer"},"ref":{"type":"string"}},"required":["action","id"],"title":"update"},{"type":"object","properties":{"action":{"const":"refresh"},"id":{"type":"integer"}},"required":["action","id"],"title":"refresh"},{"type":"object","properties":{"action":{"const":"delete"},"id":{"type":"integer"}},"required":["action","id"],"title":"delete"}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true},"id":{"type":"integer"},"error":{"type":["string","null"]}},"title":"add"},{"type":"object","properties":{"ok":{"const":true}},"title":"update"},{"type":"object","properties":{"ok":{"const":true}},"title":"refresh"},{"type":"object","properties":{"ok":{"const":true}},"title":"delete"}]}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/site-events":{"get":{"tags":["Projects and overview"],"operationId":"get_site_events","summary":"Site events of a project","description":"**Who:** Team only (owner, member or viewer); clients get 403.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"project_id","in":"query","required":true,"schema":{"type":"integer"},"description":"Project id."},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["ok","opnieuw","dubbel","mislukt","geweigerd","ongeldig"]}},{"name":"entry","in":"query","required":false,"schema":{"type":"integer"},"description":"One log entry, with runs_detail (automation, filters, actions)."}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"log":{"type":"array","items":{"type":"object"},"description":"id, event, event_id, data, private_keys (alleen namen), status, reason, http_status, runs, duration_ms, received_at; de laatste 100"},"hook":{"type":["object","null"],"description":"received, last_at, created_at"},"events":{"type":"array","items":{"type":"object"},"description":"event, total, refs (ref, n), days (day, n); 30 dagen"},"automations":{"type":"array","items":{"type":"object"},"description":"id, name, enabled, event"},"retrying":{"type":"integer"},"can_manage":{"type":"boolean"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Projects and overview"],"operationId":"post_site_events","summary":"Create or remove the site events address","description":"**Who:** Owner or member; viewers and clients get 403.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"create"},"project_id":{"type":"integer"}},"required":["action","project_id"],"title":"create","description":"A new address replaces the old one. The secret is shown once."},{"type":"object","properties":{"action":{"const":"delete"},"project_id":{"type":"integer"}},"required":["action","project_id"],"title":"delete"}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true},"url":{"type":"string"},"secret":{"type":"string"}},"title":"create"},{"type":"object","properties":{"ok":{"const":true}},"title":"delete"}]}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/project-tips":{"get":{"tags":["Projects and overview"],"operationId":"get_project_tips","summary":"What you could still set up for a project","description":"From the basics, what Studio sees on the site but is not connected, and what at least half of the projects of the same type have.\n\n**Who:** Team only (owner, member or viewer); clients get 403.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"project_id","in":"query","required":true,"schema":{"type":"integer"},"description":"Project id."},{"name":"all","in":"query","required":false,"schema":{"type":"boolean"},"description":"Include dismissed ones."}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"tips":{"type":"array","items":{"type":"object"},"description":"key, source (basis|gezien|soortgelijk), title, why, action {label, to}, peers (?object: count, total, template), dismissed"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Projects and overview"],"operationId":"post_project_tips","summary":"Dismiss or restore a tip","description":"**Who:** Team only (owner, member or viewer); clients get 403.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"dismiss"},"project_id":{"type":"integer"},"key":{"type":"string"}},"required":["action","project_id","key"],"title":"dismiss","description":"For the whole team."},{"type":"object","properties":{"action":{"const":"restore"},"project_id":{"type":"integer"},"key":{"type":"string"}},"required":["action","project_id","key"],"title":"restore"}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true},"tips":{"type":"array","items":{"type":"object"}}},"title":"dismiss"},{"type":"object","properties":{"ok":{"const":true},"tips":{"type":"array","items":{"type":"object"}}},"title":"restore"}]}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/connections":{"get":{"tags":["Sources and connections"],"operationId":"get_connections","summary":"Google connections with their GA4 properties","description":"**Who:** Team only (owner, member or viewer); clients get 403.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"connections":{"type":"array","items":{"type":"object"},"description":"id, kind, google_email, status, own_key, usable, created_at, properties"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Sources and connections"],"operationId":"post_connections","summary":"Add, replace or remove a service account","description":"**Who:** Owner or member; viewers and clients get 403.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"create"},"key":{"type":["object","string"],"description":"The service account JSON, as object or text. Tested with Google immediately."}},"required":["action","key"],"title":"create"},{"type":"object","properties":{"action":{"const":"replace_key"},"connection_id":{"type":"integer"},"key":{"type":["object","string"]}},"required":["action","connection_id","key"],"title":"replace_key","description":"Keeps the connection and its assignments; the old key stays active if the new one fails."},{"type":"object","properties":{"action":{"const":"toggle"},"property_id":{"type":"integer"},"enabled":{"type":"boolean"}},"required":["action","property_id","enabled"],"title":"toggle"},{"type":"object","properties":{"action":{"const":"refresh"}},"required":["action"],"title":"refresh","description":"Clears the workspace caches, for example after granting new Analytics access."},{"type":"object","properties":{"action":{"const":"delete"},"connection_id":{"type":"integer"}},"required":["action","connection_id"],"title":"delete"}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true},"connection_id":{"type":"integer"},"google_email":{"type":"string","format":"email"},"property_count":{"type":"integer"}},"title":"create"},{"type":"object","properties":{"ok":{"const":true},"google_email":{"type":"string","format":"email"},"property_count":{"type":"integer"}},"title":"replace_key"},{"type":"object","properties":{"ok":{"const":true}},"title":"toggle"},{"type":"object","properties":{"ok":{"const":true}},"title":"refresh"},{"type":"object","properties":{"ok":{"const":true}},"title":"delete"}]}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/connections/google":{"get":{"tags":["Sources and connections"],"operationId":"get_connections_google","summary":"Start connecting with Google (OAuth)","description":"Redirects the browser to Google for read access to Analytics, Search Console and Business Profile. Meant for the browser.\n\n**Who:** Owner or member; viewers and clients get 403.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","responses":{"302":{"description":"Redirect; the address is in Location.","headers":{"Location":{"schema":{"type":"string","format":"uri"}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/connections/google/callback":{"get":{"tags":["Sources and connections"],"operationId":"get_connections_google_callback","summary":"Return from Google after connecting","description":"**Access:** No sign-in","security":[],"x-studio-auth":"public","responses":{"200":{"description":"Success.","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/api/connections/oauth/{provider}":{"get":{"tags":["Sources and connections"],"operationId":"get_connections_oauth_provider","summary":"Start connecting with Teamleader or Microsoft Advertising (OAuth)","description":"`provider` is `teamleader` or `microsoft_ads`. Redirects the browser to the service; on return instnt studio stores the refresh token encrypted with the integration. Without an app on the server (client ID and secret in .env) the browser returns with `oauth=niet-ingericht`.\n\n**Who:** Owner or member; viewers and clients get 403.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"provider","in":"path","required":true,"schema":{"type":"string"}},{"name":"integration_id","in":"query","required":true,"schema":{"type":"integer"},"description":"An integration of kind teamleader or microsoft_ads, created first with POST /api/integrations."}],"responses":{"302":{"description":"Redirect; the address is in Location.","headers":{"Location":{"schema":{"type":"string","format":"uri"}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/connections/oauth/{provider}/callback":{"get":{"tags":["Sources and connections"],"operationId":"get_connections_oauth_provider_callback","summary":"Return from Teamleader or Microsoft after connecting","description":"**Access:** No sign-in","security":[],"x-studio-auth":"public","parameters":[{"name":"provider","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Success.","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/api/search-console":{"get":{"tags":["Sources and connections"],"operationId":"get_search_console","summary":"Search Console properties","description":"**Who:** Team only (owner, member or viewer); clients get 403.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"sites":{"type":"array","items":{"type":"object"},"description":"id, project_id, connection_id, site_url, display_name, status"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Sources and connections"],"operationId":"post_search_console","summary":"Add and assign Search Console properties","description":"**Who:** Owner or member; viewers and clients get 403.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"available"},"connection_id":{"type":"integer"}},"required":["action","connection_id"],"title":"available"},{"type":"object","properties":{"action":{"const":"create"},"project_id":{"type":"integer"},"connection_id":{"type":"integer"},"site_url":{"type":"string"}},"required":["action","project_id","connection_id","site_url"],"title":"create"},{"type":"object","properties":{"action":{"const":"update"},"site_id":{"type":"integer"},"display_name":{"type":"string"}},"required":["action","site_id"],"title":"update"},{"type":"object","properties":{"action":{"const":"assign"},"site_id":{"type":"integer"},"project_id":{"type":["integer","null"]}},"required":["action","site_id"],"title":"assign"},{"type":"object","properties":{"action":{"const":"delete"},"site_id":{"type":"integer"}},"required":["action","site_id"],"title":"delete"}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true},"sites":{"type":"array","items":{"type":"object"},"description":"site_url"}},"title":"available"},{"type":"object","properties":{"ok":{"const":true},"site_id":{"type":"integer"}},"title":"create"},{"type":"object","properties":{"ok":{"const":true}},"title":"update"},{"type":"object","properties":{"ok":{"const":true}},"title":"assign"},{"type":"object","properties":{"ok":{"const":true}},"title":"delete"}]}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/sources":{"get":{"tags":["Sources and connections"],"operationId":"get_sources","summary":"Own systems (instnt-metrics)","description":"**Who:** Team only (owner, member or viewer); clients get 403.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"sources":{"type":"array","items":{"type":"object"},"description":"id, project_id, name, url, status"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Sources and connections"],"operationId":"post_sources","summary":"Connect or change an own system","description":"**Who:** Owner or member; viewers and clients get 403.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"create"},"name":{"type":"string"},"url":{"type":"string","format":"uri","description":"https; http only for localhost."},"token":{"type":"string"},"project_id":{"type":"integer"}},"required":["action","name","url","token"],"title":"create","description":"Tested against the instnt-metrics contract immediately; `422 source_rejected` with the reason when it fails."},{"type":"object","properties":{"action":{"const":"update"},"source_id":{"type":"integer"},"name":{"type":"string"},"url":{"type":"string","format":"uri"},"token":{"type":"string","description":"Empty keeps the current token."}},"required":["action","source_id","name"],"title":"update"},{"type":"object","properties":{"action":{"const":"assign"},"source_id":{"type":"integer"},"project_id":{"type":["integer","null"]}},"required":["action","source_id"],"title":"assign"},{"type":"object","properties":{"action":{"const":"delete"},"source_id":{"type":"integer"}},"required":["action","source_id"],"title":"delete"}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true},"source_id":{"type":"integer"},"preview":{"type":"object"}},"title":"create"},{"type":"object","properties":{"ok":{"const":true}},"title":"update"},{"type":"object","properties":{"ok":{"const":true}},"title":"assign"},{"type":"object","properties":{"ok":{"const":true}},"title":"delete"}]}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/integrations":{"get":{"tags":["Sources and connections"],"operationId":"get_integrations","summary":"Extra services of a project","description":"`oauth` only appears for teamleader and microsoft_ads: `connected` (a token exists) and `available` (the server can connect).\n\n**Who:** Team; clients with access to the Website module.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"project_id","in":"query","required":true,"schema":{"type":"integer"},"description":"Project id."}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"integrations":{"type":"array","items":{"type":"object"},"description":"id, project_id, kind, name, status, config, oauth"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Sources and connections"],"operationId":"post_integrations","summary":"Connect, test or change a service","description":"**Who:** Owner or member; viewers and clients get 403.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"create"},"project_id":{"type":"integer"},"kind":{"type":"string","description":"For example clarity, sentry, umami, posthog, mollie, stripe, github, bing, dataforseo, shopify, woocommerce, appstore_connect. The full list is in the source marketplace."},"config":{"type":"object","description":"For appstore_connect: `account_id` and `app_id` (see /api/appstore-accounts). A config with issuer_id, key_id and private_key becomes an Apple account."},"name":{"type":"string"}},"required":["action","project_id","kind","config"],"title":"create"},{"type":"object","properties":{"action":{"const":"update"},"integration_id":{"type":"integer"},"config":{"type":"object","description":"An empty secret field keeps the current secret."}},"required":["action","integration_id","config"],"title":"update"},{"type":"object","properties":{"action":{"const":"test"},"integration_id":{"type":"integer"}},"required":["action","integration_id"],"title":"test","description":"Fetches once right away."},{"type":"object","properties":{"action":{"const":"appstore_analytics"},"integration_id":{"type":"integer"},"key_id":{"type":"string","description":"Key ID of an App Store Connect key with the Admin role."},"private_key":{"type":"string","description":"Contents of that key’s .p8 file."}},"required":["action","integration_id","key_id","private_key"],"title":"appstore_analytics","description":"Only for an App Store Connect integration: requests App Analytics from Apple once. The Admin key is used only for this request and is not stored."},{"type":"object","properties":{"action":{"const":"delete"},"integration_id":{"type":"integer"}},"required":["action","integration_id"],"title":"delete"}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true},"integration_id":{"type":"integer"}},"title":"create"},{"type":"object","properties":{"ok":{"const":true}},"title":"update"},{"type":"object","properties":{"ok":{"const":true},"testable":{"type":"boolean"},"passed":{"type":"boolean"},"metrics":{"type":"object"},"items":{"type":"array","items":{"type":"string"}},"error":{"type":"string"}},"title":"test"},{"type":"object","properties":{"ok":{"const":true},"analytics":{"type":"boolean"}},"title":"appstore_analytics"},{"type":"object","properties":{"ok":{"const":true}},"title":"delete"}]}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/appstore-accounts":{"get":{"tags":["Sources and connections"],"operationId":"get_appstore_accounts","summary":"Apple accounts for App Store Connect","description":"Per account the team’s apps and which project each app belongs to. The .p8 key is never returned.\n\n**Who:** Team only (owner, member or viewer); clients get 403.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"accounts":{"type":"array","items":{"type":"object"},"description":"id, name, issuer_id, key_id, vendor_number, key_configured, status, status_message, checked_at, history (running, pending), keys (id, role, role_label, key_id, status, status_message, denied), webhook (url, secret; alleen voor beheerders), apps (id, name, bundle_id, sku, downloads_total, downloads_complete, downloads_30d, project_id, project_name, integration_id, analytics)"},"projects":{"type":"array","items":{"type":"object"},"description":"id, name"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Sources and connections"],"operationId":"post_appstore_accounts","summary":"Add an Apple account, link apps or turn on App Analytics","description":"**Who:** Owner or member; viewers and clients get 403.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"create"},"issuer_id":{"type":"string"},"key_id":{"type":"string"},"private_key":{"type":"string","description":"Contents of the .p8 file (Sales and Reports role)."},"vendor_number":{"type":"string","description":"One or more vendor numbers, separated by commas."},"name":{"type":"string"}},"required":["action","issuer_id","key_id","private_key"],"title":"create","description":"Tests the key with Apple first by fetching the apps; a key Apple rejects is not stored. The same issuer and key ID update the existing account."},{"type":"object","properties":{"action":{"const":"update"},"account_id":{"type":"integer"},"name":{"type":"string"},"issuer_id":{"type":"string","description":"A changed ID is tested at Apple first."},"key_id":{"type":"string","description":"A changed ID is tested at Apple first."},"vendor_number":{"type":"string","description":"One or more, separated by commas. A different number fetches the history again."},"private_key":{"type":"string","description":"Empty keeps the current key."}},"required":["action","account_id"],"title":"update"},{"type":"object","properties":{"action":{"const":"refresh"},"account_id":{"type":"integer"}},"required":["action","account_id"],"title":"refresh","description":"Fetches the apps from Apple again."},{"type":"object","properties":{"action":{"const":"link"},"account_id":{"type":"integer"},"app_id":{"type":"string"},"project_id":{"type":["integer","null"],"description":"Empty or null unlinks the app."}},"required":["action","account_id","app_id"],"title":"link","description":"An app belongs to at most one project, and a project has one App Store Connect source: a new link replaces the old one."},{"type":"object","properties":{"action":{"const":"history"},"account_id":{"type":"integer"}},"required":["action","account_id"],"title":"history","description":"Fetches the missing sales reports: years (ten years back), months and days (one year). Also happens automatically after adding and twice a day; Studio never requests a closed period again."},{"type":"object","properties":{"action":{"const":"analytics"},"account_id":{"type":"integer"},"key_id":{"type":"string","description":"Key ID of a key with the Admin role."},"private_key":{"type":"string"},"app_ids":{"type":"array","items":{"type":"string"},"description":"Empty means all apps of the account."}},"required":["action","account_id","key_id","private_key"],"title":"analytics","description":"Requests App Analytics per app. The Admin key is used only for these requests and is not stored."},{"type":"object","properties":{"action":{"const":"delete"},"account_id":{"type":"integer"}},"required":["action","account_id"],"title":"delete","description":"`409 in_use` while apps are linked to projects."},{"type":"object","properties":{"action":{"const":"key_add"},"account_id":{"type":"integer"},"role":{"type":"string","enum":["sales","developer","customer_support","marketing","app_manager","admin"]},"key_id":{"type":"string"},"private_key":{"type":"string"}},"required":["action","account_id","role","key_id","private_key"],"title":"key_add","description":"Extra key with its own role, from the same team (same issuer ID). Tested with Apple first. Studio picks per section the least-privileged key that is allowed, and remembers a refusal (403) for a week. Developer opens build details, TestFlight feedback, diagnostics, Xcode Cloud, Game Center and purchases; Customer Support review replies and the store page name and subtitle."},{"type":"object","properties":{"action":{"const":"key_delete"},"account_id":{"type":"integer"},"key_id":{"type":"integer","description":"The id from `keys`, not Apple's key ID."}},"required":["action","account_id","key_id"],"title":"key_delete"},{"type":"object","properties":{"action":{"const":"webhook"},"account_id":{"type":"integer"}},"required":["action","account_id"],"title":"webhook","description":"New address and secret for App Store Connect notifications (Users and Access → Integrations → Webhooks). An old address stops working."}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true},"account_id":{"type":"integer"},"apps":{"type":"integer"}},"title":"create"},{"type":"object","properties":{"ok":{"const":true}},"title":"update"},{"type":"object","properties":{"ok":{"const":true},"apps":{"type":"integer"}},"title":"refresh"},{"type":"object","properties":{"ok":{"const":true},"integration_id":{"type":["integer","null"]}},"title":"link"},{"type":"object","properties":{"ok":{"const":true},"started":{"type":"boolean"},"pending":{"type":"integer"}},"title":"history"},{"type":"object","properties":{"ok":{"const":true},"enabled":{"type":"array","items":{"type":"string"}},"failed":{"type":"object","description":"app_id → melding"}},"title":"analytics"},{"type":"object","properties":{"ok":{"const":true}},"title":"delete"},{"type":"object","properties":{"ok":{"const":true}},"title":"key_add"},{"type":"object","properties":{"ok":{"const":true}},"title":"key_delete"},{"type":"object","properties":{"ok":{"const":true},"webhook":{"type":"object","description":"url, secret"}},"title":"webhook"}]}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/google-play-accounts":{"get":{"tags":["Sources and connections"],"operationId":"get_google_play_accounts","summary":"Google Play accounts","description":"Per account the apps the service account can see, with installs and which project each app belongs to. The key is never returned, only the service account email.\n\n**Who:** Team only (owner, member or viewer); clients get 403.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"accounts":{"type":"array","items":{"type":"object"},"description":"id, name, client_email, bucket, status, status_message, checked_at, apps (package_name, title, installs_total, installs_30d, project_id, project_name, state)"},"projects":{"type":"array","items":{"type":"object"},"description":"id, name"},"can_manage":{"type":"boolean"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Sources and connections"],"operationId":"post_google_play_accounts","summary":"Add a Google Play account or link apps","description":"**Who:** Owner or member; viewers and clients get 403.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"create"},"key":{"type":"string","description":"The JSON key of a Google Cloud service account invited in Play Console."},"bucket":{"type":"string","description":"Cloud Storage URI of the reports (gs://pubsite_prod_…). Without a bucket no installs."},"name":{"type":"string"}},"required":["action","key"],"title":"create","description":"Tests the key with Google first by fetching the apps; a key that is not allowed anything is not stored. The same service account updates the existing account."},{"type":"object","properties":{"action":{"const":"update"},"account_id":{"type":"integer"},"name":{"type":"string"},"bucket":{"type":"string"},"key":{"type":"string","description":"Only a new key of the same service account."}},"required":["action","account_id"],"title":"update"},{"type":"object","properties":{"action":{"const":"refresh"},"account_id":{"type":"integer"}},"required":["action","account_id"],"title":"refresh","description":"Fetch apps and install reports again; at most once a minute."},{"type":"object","properties":{"action":{"const":"link"},"account_id":{"type":"integer"},"package_name":{"type":"string"},"project_id":{"type":["integer","null"],"description":"Empty or null unlinks the app."}},"required":["action","account_id","package_name"],"title":"link","description":"An app belongs to at most one project; a first fetch (installs, vitals, reviews, versions) follows immediately."},{"type":"object","properties":{"action":{"const":"delete"},"account_id":{"type":"integer"}},"required":["action","account_id"],"title":"delete","description":"Also removes the app links to projects. The fetched installs go too."}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true},"account_id":{"type":"integer"},"apps":{"type":"integer"}},"title":"create"},{"type":"object","properties":{"ok":{"const":true}},"title":"update"},{"type":"object","properties":{"ok":{"const":true},"apps":{"type":"integer"},"files":{"type":"integer"}},"title":"refresh"},{"type":"object","properties":{"ok":{"const":true},"linked":{"type":"boolean"},"integration_id":{"type":"integer"}},"title":"link"},{"type":"object","properties":{"ok":{"const":true}},"title":"delete"}]}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/google-play":{"get":{"tags":["Sources and connections"],"operationId":"get_google_play","summary":"Google Play: installs, vitals and versions of an app","description":"Per Google Play app of the project: user installs per day (365 days) and month from the monthly reports in Cloud Storage, active devices, rating, Android vitals (user-perceived crashes and ANR, averaged over 28 days, with Google's threshold) and the versions per track. A part switched off on the account returns `null`. `null` for a day means unknown, not zero. Reviews are under /api/reputation. Requires the Product module.\n\n**Who:** Everyone in the workspace; a client only sees their own projects.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"project_id","in":"query","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"apps":{"type":"array","items":{"type":"object"},"description":"account_id, package_name, name, store_url, bucket, installs (total (downloads, complete), d7, d30, prev30, uninstalls_30d, active_devices, active_on, rating, days (day, value), months (month, value, running), first_month) of null, vitals (user_crash_rate, user_anr_rate, crash_rate, anr_rate, crash_limit, anr_limit, crash_bad, anr_bad, until, days (day, crash, anr)) of null, tracks (track, label, releases (name, status, version_codes, user_fraction, notes)) of null, tracks_at, fetched_at"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/app-store":{"get":{"tags":["Sources and connections"],"operationId":"get_app_store","summary":"App Store for makers: versions, TestFlight, reviews, performance, subscriptions and store page","description":"Per app of the project what App Store Connect knows, fetched every half hour (versions, builds, beta groups, feedback, reviews) and once a day (the rest). `sections` gives the state per section: `ok`, `leeg` (empty), `fout` (error), or `sleutel` with the needed role in `message`. Subscriptions only for the team or a client allowed to approve the overview. Requires the Product module.\n\n**Who:** Everyone in the workspace; a client only sees their own projects.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"project_id","in":"query","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"apps":{"type":"array","items":{"type":"object"},"description":"account_id, app_id, name, roles, sections, versions, testflight (builds, groups, feedback), reviews (items, count, average, per_rating, open, territories), performance (versions, metrics, insights, diagnostics), subscriptions (events_30d, active, trials, proceeds_30d, days, catalog) of null, store (current, changes), ci, game_center"},"can_reply":{"type":"boolean"},"team":{"type":"boolean"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Sources and connections"],"operationId":"post_app_store","summary":"Reply to reviews, crash as prompt, fetch now","description":"**Who:** Owner or member; viewers and clients get 403.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"reply"},"project_id":{"type":"integer"},"review_id":{"type":"integer","description":"The `id` from reviews.items."},"body":{"type":"string","description":"At most 5970 characters."}},"required":["action","project_id","review_id","body"],"title":"reply","description":"Posts or replaces the reply at Apple. Requires a key with the Customer Support or App Manager role."},{"type":"object","properties":{"action":{"const":"delete_reply"},"project_id":{"type":"integer"},"review_id":{"type":"integer"}},"required":["action","project_id","review_id"],"title":"delete_reply"},{"type":"object","properties":{"action":{"const":"suggest_reply"},"project_id":{"type":"integer"},"review_id":{"type":"integer"}},"required":["action","project_id","review_id"],"title":"suggest_reply","description":"Claude drafts a reply in the brand voice; it then appears as `suggestion` on the review. Never posted automatically."},{"type":"object","properties":{"action":{"const":"prompt"},"project_id":{"type":"integer"},"feedback_id":{"type":"integer"}},"required":["action","project_id","feedback_id"],"title":"prompt","description":"Prompt for a Claude Code session in the app repository, with the crash log."},{"type":"object","properties":{"action":{"const":"sync"},"project_id":{"type":"integer"}},"required":["action","project_id"],"title":"sync","description":"At most once a minute (429)."}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true},"response":{"type":"object","description":"id, body, state, modified"}},"title":"reply"},{"type":"object","properties":{"ok":{"const":true}},"title":"delete_reply"},{"type":"object","properties":{"ok":{"const":true},"task_id":{"type":"integer"}},"title":"suggest_reply"},{"type":"object","properties":{"ok":{"const":true},"prompt":{"type":"string"}},"title":"prompt"},{"type":"object","properties":{"ok":{"const":true},"started":{"type":"boolean"}},"title":"sync"}]}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/modules":{"get":{"tags":["Sources and connections"],"operationId":"get_modules","summary":"Modules and their state","description":"Module catalogue with, per module, whether it is in the plan, on for the workspace and (with `project_id`) for the project, and whether the required source is connected. `status`: `aan` (on), `uit` (off), `plan`, `project_uit` (off for the project) or `nodig` (source needed).\n\n**Who:** Team; a client only with project_id, and then only on or off per module.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"project_id","in":"query","required":false,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"modules":{"type":"array","items":{"type":"object"},"description":"key, label, uitleg, icon, min_plan, needs_label, scope, plan_allows, workspace_on, project_on, needs_met, projects_ready, status"},"can_manage":{"type":"boolean"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Sources and connections"],"operationId":"post_modules","summary":"Turn a module on or off","description":"**Who:** Owner or member; viewers and clients get 403.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"workspace"},"module":{"type":"string"},"enabled":{"type":"boolean"}},"required":["action","module","enabled"],"title":"workspace","description":"`402 plan_limit` when the module is not in the plan."},{"type":"object","properties":{"action":{"const":"project"},"project_id":{"type":"integer"},"module":{"type":"string"},"enabled":{"type":"boolean"}},"required":["action","project_id","module","enabled"],"title":"project","description":"A project can only turn a module off; on follows the workspace."}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true},"modules":{"type":"array","items":{"type":"object"}}},"title":"workspace"},{"type":"object","properties":{"ok":{"const":true},"modules":{"type":"array","items":{"type":"object"}}},"title":"project"}]}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/orders":{"get":{"tags":["Sources and connections"],"operationId":"get_orders","summary":"Orders","description":"Individual orders from Shopify, WooCommerce and own systems, without customer data, with a link to the order in the shop. Only when the Orders module is on; only for the team or a client allowed to approve the overview. With `scope=today` the orders of today per project.\n\n**Who:** Team; clients with access to the Overview module.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"project_id","in":"query","required":false,"schema":{"type":"integer"}},{"name":"scope","in":"query","required":false,"schema":{"type":"string","enum":["today"]}},{"name":"days","in":"query","required":false,"schema":{"type":"integer"}},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["open","betaald","verzonden","afgerond","geannuleerd","terugbetaald","mislukt"]}},{"name":"q","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"orders":{"type":"array","items":{"type":"object"},"description":"id, source, number, ordered_at, status, total_cents, currency, items (name, qty), admin_url"},"counts":{"type":"object"},"sold":{"type":"integer"},"total_cents":{"type":"integer"},"module":{"type":["object","null"]},"synced_at":{"type":["string","null"],"format":"date-time"},"projects":{"type":"array","items":{"type":"object"},"description":"Only with scope=today: project_id, name, count, total_cents, currency, latest."}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Sources and connections"],"operationId":"post_orders","summary":"Fetch orders now","description":"**Who:** Owner or member; viewers and clients get 403.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"refresh"},"project_id":{"type":"integer"}},"required":["action","project_id"],"title":"refresh"}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true},"orders":{"type":"integer"},"errors":{"type":"array","items":{"type":"string"}}},"title":"refresh"}]}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/landing-pages":{"get":{"tags":["Sources and connections"],"operationId":"get_landing_pages","summary":"Landing pages module","description":"Designated landing pages with visits (GA4, else the site script), conversion, ad costs of linked Ads campaigns, forms, scroll depth (Clarity), Search Console and speed, plus A/B comparisons with the probability that B converts better (Beta-Binomial, from 100 visits per variant). Only when the module is on. Ad costs only for the team or a client allowed to approve the overview. With `page_id` one page with channels, queries and a daily series.\n\n**Who:** Team; clients with access to the Website module.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"project_id","in":"query","required":true,"schema":{"type":"integer"},"description":"Project id."},{"name":"days","in":"query","required":false,"schema":{"type":"integer"},"description":"Period in days, 7–365; defaults to 28."},{"name":"page_id","in":"query","required":false,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"pages":{"type":"array","items":{"type":"object"},"description":"id, url, path, name, status, utm_campaign, ads_campaign_ids, thumbnail, visits, visits_source, conversions, conversion_source, conversion_rate, ads, cost_per_conversion_cents, funnel, forms, scroll_depth, search, speed"},"tests":{"type":"array","items":{"type":"object"},"description":"id, name, page_a_id, page_b_id, started_on, ended_on, prob_b_better, min_sessions, verdict"},"suggestions":{"type":"array","items":{"type":"object"},"description":"path, sessions, key_events"},"campaigns":{"type":"array","items":{"type":"object"},"description":"id, name, channel"},"module":{"type":"object"},"can_manage":{"type":"boolean"},"money":{"type":"boolean"},"base_url":{"type":["string","null"],"format":"uri"},"page":{"type":"object","description":"Only with page_id: the page with channels, queries and series."}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Sources and connections"],"operationId":"post_landing_pages","summary":"Designate and compare landing pages","description":"**Who:** Owner or member (manages Website); others get 404.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"create"},"project_id":{"type":"integer"},"url":{"type":"string","description":"Path (/offerte) or full address on the project site."},"name":{"type":"string"},"status":{"type":"string","enum":["live","concept","gestopt"]},"utm_campaign":{"type":"string"},"ads_campaign_ids":{"type":"array","items":{"type":"string"}}},"required":["action","project_id","url"],"title":"create"},{"type":"object","properties":{"action":{"const":"update"},"project_id":{"type":"integer"},"page_id":{"type":"integer"},"name":{"type":"string"},"status":{"type":"string","enum":["live","concept","gestopt"]},"utm_campaign":{"type":"string"},"ads_campaign_ids":{"type":"array","items":{"type":"string"}}},"required":["action","project_id","page_id"],"title":"update"},{"type":"object","properties":{"action":{"const":"delete"},"project_id":{"type":"integer"},"page_id":{"type":"integer"}},"required":["action","project_id","page_id"],"title":"delete"},{"type":"object","properties":{"action":{"const":"test_create"},"project_id":{"type":"integer"},"page_a_id":{"type":"integer"},"page_b_id":{"type":"integer"},"name":{"type":"string"}},"required":["action","project_id","page_a_id","page_b_id"],"title":"test_create"},{"type":"object","properties":{"action":{"const":"test_end"},"project_id":{"type":"integer"},"test_id":{"type":"integer"}},"required":["action","project_id","test_id"],"title":"test_end"},{"type":"object","properties":{"action":{"const":"test_delete"},"project_id":{"type":"integer"},"test_id":{"type":"integer"}},"required":["action","project_id","test_id"],"title":"test_delete"}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true},"id":{"type":"integer"}},"title":"create"},{"type":"object","properties":{"ok":{"const":true}},"title":"update"},{"type":"object","properties":{"ok":{"const":true}},"title":"delete"},{"type":"object","properties":{"ok":{"const":true},"id":{"type":"integer"}},"title":"test_create"},{"type":"object","properties":{"ok":{"const":true}},"title":"test_end"},{"type":"object","properties":{"ok":{"const":true}},"title":"test_delete"}]}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/submissions":{"get":{"tags":["Sources and connections"],"operationId":"get_submissions","summary":"Form submissions","description":"Which form was submitted when, on which page, with a link to the submission in the CMS or form tool. Never what was filled in. Only when the Submissions module is on. `admin_url` only for the team or a client with Website on approve; otherwise `null`. `new`: after the last submission this user viewed and within the last 30 days. With `scope=new` the new submissions per project.\n\n**Who:** Team; clients with access to the Website module.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"project_id","in":"query","required":false,"schema":{"type":"integer"}},{"name":"scope","in":"query","required":false,"schema":{"type":"string","enum":["new"]}},{"name":"days","in":"query","required":false,"schema":{"type":"integer"},"description":"Period in days, 1–396; defaults to 30."},{"name":"form","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"submissions":{"type":"array","items":{"type":"object"},"description":"id, source, source_label, form, page, submitted_at, new, admin_url"},"forms":{"type":"array","items":{"type":"object"},"description":"form, count, latest_at"},"total":{"type":"integer"},"unread":{"type":"integer"},"can_open":{"type":"boolean"},"module":{"type":"object"},"projects":{"type":"array","items":{"type":"object"},"description":"Only with scope=new: project_id, name, count, last_id, latest_at."}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Sources and connections"],"operationId":"post_submissions","summary":"Mark submissions as seen","description":"**Who:** Team; clients with access to the Website module.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"seen"},"project_id":{"type":"integer"}},"required":["action","project_id"],"title":"seen","description":"Per user: everything so far in this project no longer counts as new."}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true},"seen_id":{"type":"integer"}},"title":"seen"}]}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/reputation":{"get":{"tags":["Sources and connections"],"operationId":"get_reputation","summary":"Reputation: reviews from all sources","description":"Reviews from the Google Business Profile, Trustpilot, Kiyoh and the App Store together. `summary.average` and `count` come from the platforms themselves where known (Kiyoh converted from 10 to 5), the rest from stored reviews (the latest 50 to 100 per source). `trend` per month over twelve months. Claude suggestions (`suggestion`) only for the team. Without `project_id`: per project you can see under Findability.\n\n**Who:** Team; clients with access to the Search module.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"project_id","in":"query","required":false,"schema":{"type":"integer"}},{"name":"source","in":"query","required":false,"schema":{"type":"string","enum":["gbp","trustpilot","kiyoh","appstore","google_play"]}},{"name":"rating","in":"query","required":false,"schema":{"type":"integer"},"description":"Only this number of stars, 1–5."},{"name":"open","in":"query","required":false,"schema":{"type":"boolean"},"description":"Only without a reply."}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"summary":{"type":"object","description":"average, count, new_this_month, answered_pct, open_low, stored"},"sources":{"type":"array","items":{"type":"object"},"description":"source, label, average, count, stored, platform, can_reply"},"trend":{"type":"array","items":{"type":"object"},"description":"month, count, average"},"items":{"type":"array","items":{"type":"object"},"description":"id, source, source_label, rating, score, title, body, author, language, country, url, reviewed_at, reply, can_reply, suggestion"},"can_reply":{"type":"boolean"},"team":{"type":"boolean"},"projects":{"type":"array","items":{"type":"object"},"description":"Only without project_id: project_id, project, sources, average, count, new_this_month, answered_pct, open_low."}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Sources and connections"],"operationId":"post_reputation","summary":"Reply to reviews","description":"**Who:** Owner or member; viewers and clients get 403.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"reply"},"project_id":{"type":"integer"},"review_id":{"type":"integer","description":"The `id` from items."},"body":{"type":"string","description":"Google at most 4096 characters, the App Store 5970."}},"required":["action","project_id","review_id","body"],"title":"reply","description":"Google and the App Store only; Trustpilot and Kiyoh return `422`. Google requires \"Connect with Google\" with Reply to reviews enabled."},{"type":"object","properties":{"action":{"const":"delete_reply"},"project_id":{"type":"integer"},"review_id":{"type":"integer"}},"required":["action","project_id","review_id"],"title":"delete_reply"},{"type":"object","properties":{"action":{"const":"suggest_reply"},"project_id":{"type":"integer"},"review_id":{"type":"integer"}},"required":["action","project_id","review_id"],"title":"suggest_reply","description":"Claude drafts a reply in the brand voice; it then appears as `suggestion` on the review. Never posted automatically."}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true},"reply":{"type":"object","description":"body, at, state"}},"title":"reply"},{"type":"object","properties":{"ok":{"const":true}},"title":"delete_reply"},{"type":"object","properties":{"ok":{"const":true},"task_id":{"type":"integer"}},"title":"suggest_reply"}]}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/campaigns":{"get":{"tags":["Sources and connections"],"operationId":"get_campaigns","summary":"Campaigns: UTM, ads and mailings together","description":"Campaigns module (from Copiloot). A campaign adds up what matches its rules: GA4 per UTM campaign, source and medium, sign-ups and payments from your own system with the same UTM, Google Ads, Meta and LinkedIn campaigns, and mailings from Mailchimp and Brevo. Rules of the same kind: one must match; different UTM kinds: all. `previous` is an equally long period directly before. Users are summed per day. Cost, revenue, budget, CPC, CPA and ROAS only for the team or a client who may approve the overview. Without `project_id`: all projects with the module that you can see. With `campaign_id` also `series`, `channels`, `sources`, `pages`, `ads` and `mailings`.\n\n**Who:** Team; clients with access to the Website module.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"project_id","in":"query","required":false,"schema":{"type":"integer"}},{"name":"campaign_id","in":"query","required":false,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"campaigns":{"type":"array","items":{"type":"object"},"description":"id, project_id, name, starts_on, ends_on, budget, goal, rules, period, current, previous, derived, mail"},"suggestions":{"type":"array","items":{"type":"object"},"description":"UTM campaigns without a campaign (90 days): value, sessions, sources. Manage rights only."},"ignored":{"type":"array","items":{"type":"string"}},"linkables":{"type":"array","items":{"type":"object"},"description":"What you can link as a rule: kind, value, label."},"module":{"type":"object"},"money":{"type":"boolean"},"can_manage":{"type":"boolean"},"base_url":{"type":["string","null"]},"campaign":{"type":"object","description":"With campaign_id."},"projects":{"type":"array","items":{"type":"object"},"description":"Without project_id: project_id, name, campaigns."}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Sources and connections"],"operationId":"post_campaigns","summary":"Manage campaigns","description":"Sending is not possible here: do that in Brevo or Mailchimp; instnt studio reads the results.\n\n**Who:** Owner or member (manages Website); others get 404.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"create"},"project_id":{"type":"integer"},"name":{"type":"string"},"starts_on":{"type":["string","null"],"format":"date"},"ends_on":{"type":["string","null"],"format":"date"},"budget":{"type":["number","null"],"description":"In euros."},"goal":{"type":["string","null"]},"rules":{"type":"array","items":{"type":"object"},"description":"kind (utm_campaign|utm_source|utm_medium|google_ads|meta_ads|linkedin_ads|mail), match (exact|prefix|contains, UTM only), value. An ad campaign or mailing must belong to the project. At most 30."}},"required":["action","project_id","name"],"title":"create","description":"The first campaign of a project fetches the GA4 figures per campaign right away (half a year back)."},{"type":"object","properties":{"action":{"const":"update"},"project_id":{"type":"integer"},"campaign_id":{"type":"integer"},"name":{"type":"string"},"starts_on":{"type":["string","null"],"format":"date"},"ends_on":{"type":["string","null"],"format":"date"},"budget":{"type":["number","null"]},"goal":{"type":["string","null"]},"rules":{"type":"array","items":{"type":"object"},"description":"Replaces all rules."}},"required":["action","project_id","campaign_id"],"title":"update"},{"type":"object","properties":{"action":{"const":"delete"},"project_id":{"type":"integer"},"campaign_id":{"type":"integer"}},"required":["action","project_id","campaign_id"],"title":"delete"},{"type":"object","properties":{"action":{"const":"ignore"},"project_id":{"type":"integer"},"value":{"type":"string"}},"required":["action","project_id","value"],"title":"ignore","description":"Stop suggesting this UTM campaign."},{"type":"object","properties":{"action":{"const":"unignore"},"project_id":{"type":"integer"},"value":{"type":"string"}},"required":["action","project_id","value"],"title":"unignore"}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true},"id":{"type":"integer"}},"title":"create"},{"type":"object","properties":{"ok":{"const":true}},"title":"update"},{"type":"object","properties":{"ok":{"const":true}},"title":"delete"},{"type":"object","properties":{"ok":{"const":true}},"title":"ignore"},{"type":"object","properties":{"ok":{"const":true}},"title":"unignore"}]}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/journey":{"get":{"tags":["Sources and connections"],"operationId":"get_journey","summary":"Customer journey: stages, channels and paths as totals","description":"Customer journey module (from Inzicht). Added up nightly from GA4, the site script, leads, sign-ups from your own system and orders; no journey per person. `stages`: visit, engaged, lead and customer with the sources that count (per project type, or as configured), the previous equally long period and `from_previous` (a ratio of totals, not a tracked group). `channels`: channel × stage; a stage without channel information is listed in `missing`. `paths`: steps from page to page, pages with a lead and landing pages with key events, only at least `k` times; `hidden` counts what stayed below. `ttc`: days from sign-up to subscription, only with at least `k` subscriptions. Without the module: `journey` is null.\n\n**Who:** Team; clients with access to the Website module.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"project_id","in":"query","required":true,"schema":{"type":"integer"},"description":"Project id."},{"name":"period","in":"query","required":false,"schema":{"type":"integer"},"description":"28 or 90; defaults to 28."}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"module":{"type":"object"},"can_manage":{"type":"boolean"},"journey":{"type":["object","null"],"description":"type, type_label, period, range, stages, trend, channels, paths, ttc, sources, custom, stage_labels, computed_at, k"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Sources and connections"],"operationId":"post_journey","summary":"Configure the customer journey","description":"**Who:** Owner or member (manages Website); others get 404.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"settings"},"project_id":{"type":"integer"},"stages":{"type":["object","null"],"description":"Per stage (bezoek, betrokken, lead, klant) a list of sources from `sources`; null restores the default for the project type."}},"required":["action","project_id"],"title":"settings"},{"type":"object","properties":{"action":{"const":"recompute"},"project_id":{"type":"integer"}},"required":["action","project_id"],"title":"recompute","description":"Queue a recount; at most once every five minutes (`429 too_soon`)."}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true},"stages":{"type":"object"}},"title":"settings"},{"type":"object","properties":{"ok":{"const":true},"queued":{"type":"boolean"}},"title":"recompute"}]}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/traffic":{"get":{"tags":["Sources and connections"],"operationId":"get_traffic","summary":"AI traffic and clicks against sessions, per day","description":"From the fact tables, updated nightly. `view=ai`: sessions from AI assistants (ChatGPT, Perplexity, Gemini, Copilot, Claude …) from GA4, or without GA4 visitors from the site script (`unit`); `previous` is the equally long period before. `view=search`: Search Console clicks against organic GA4 sessions per day. Search Console lags a few days; those days have `clicks: null`. `totals` only counts days where both exist.\n\n**Who:** `view=ai`: view Visits (website); `view=search`: view Findability (search). Others get 404.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"project_id","in":"query","required":true,"schema":{"type":"integer"},"description":"Project id."},{"name":"view","in":"query","required":false,"schema":{"type":"string","enum":["ai","search"]}},{"name":"days","in":"query","required":false,"schema":{"type":"integer"},"description":"7, 28 or 90; defaults to 28."}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"days":{"type":"integer"},"source":{"type":["string","null"],"description":"Only with `ai`: ga4, script or null."},"unit":{"type":"string"},"current":{"type":"integer"},"previous":{"type":"integer"},"series":{"type":"array","items":{"type":"object"},"description":"day, value (ai) of day, clicks, sessions (search)"},"sources":{"type":"array","items":{"type":"object"},"description":"key, value, previous"},"has_search":{"type":"boolean"},"has_ga":{"type":"boolean"},"search_until":{"type":["string","null"],"format":"date"},"ga_until":{"type":["string","null"],"format":"date","description":"Last day with GA4 figures; after it `sessions: null`."},"totals":{"type":"object","description":"clicks, sessions, ratio"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/not-found":{"get":{"tags":["Sources and connections"],"operationId":"get_not_found","summary":"404 monitor","description":"Pages that do not exist but get visits or links, merged every morning from GA4 (title or path of the error page), the site script, the crawler and Search Console URL inspection. The Search Console API has no list of 404s; only per inspection. A page not seen for seven days is marked resolved; when it gets visits again, it is new again.\n\n**Who:** Team; clients with access to the Search module.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"project_id","in":"query","required":true,"schema":{"type":"integer"},"description":"Project id."},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["nieuw","opgelost","genegeerd"]}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"status":{"type":"string"},"counts":{"type":"object","description":"nieuw, opgelost, genegeerd"},"pages":{"type":"array","items":{"type":"object"},"description":"id, path, status, hits, sources, referrers, first_seen, last_seen, resolved_at"},"settings":{"type":"object","description":"titles, paths, min_hits"},"can_manage":{"type":"boolean"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Sources and connections"],"operationId":"post_not_found","summary":"Update the 404 monitor","description":"**Who:** Owner or member (manages Search); others get 404.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"ignore"},"project_id":{"type":"integer"},"id":{"type":"integer"}},"required":["action","project_id","id"],"title":"ignore"},{"type":"object","properties":{"action":{"const":"reopen"},"project_id":{"type":"integer"},"id":{"type":"integer"}},"required":["action","project_id","id"],"title":"reopen"},{"type":"object","properties":{"action":{"const":"resolve"},"project_id":{"type":"integer"},"id":{"type":"integer"}},"required":["action","project_id","id"],"title":"resolve"},{"type":"object","properties":{"action":{"const":"settings"},"project_id":{"type":"integer"},"titles":{"type":"array","items":{"type":"string"},"description":"Words in the title of the error page, at most 10."},"paths":{"type":"array","items":{"type":"string"},"description":"Paths that always count as an error page, such as /404."},"min_hits":{"type":"integer","description":"From this many views in 28 days a new 404 triggers a notification."}},"required":["action","project_id"],"title":"settings"}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true},"id":{"type":"integer"},"status":{"type":"string"}},"title":"ignore"},{"type":"object","properties":{"ok":{"const":true},"id":{"type":"integer"},"status":{"type":"string"}},"title":"reopen"},{"type":"object","properties":{"ok":{"const":true},"id":{"type":"integer"},"status":{"type":"string"}},"title":"resolve"},{"type":"object","properties":{"ok":{"const":true},"settings":{"type":"object"}},"title":"settings"}]}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/extra-sources":{"get":{"tags":["Sources and connections"],"operationId":"get_extra_sources","summary":"Metrics from the extra services","description":"With a 30-day history. Money, deals and errors (Moneybird, Sentry, shops, CRM, ads, App Store Connect) only for the team or clients with approval rights. `analytics` only appears for App Store Connect: whether App Analytics has been requested.\n\n**Who:** Team; clients with access to the Website module.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"project_id","in":"query","required":true,"schema":{"type":"integer"},"description":"Project id."}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"can_manage":{"type":"boolean"},"sources":{"type":"array","items":{"type":"object"},"description":"id, kind, name, status, metrics, items, analytics"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/conversion-sources":{"get":{"tags":["Sources and connections"],"operationId":"get_conversion_sources","summary":"Conversion event sources","description":"**Who:** Team only (owner, member or viewer); clients get 403.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"project_id","in":"query","required":true,"schema":{"type":"integer"},"description":"Project id."}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"sources":{"type":"array","items":{"type":"object"},"description":"id, app_source_id, name, retention_days"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Sources and connections"],"operationId":"post_conversion_sources","summary":"Create a conversion key","description":"**Who:** Owner or member; viewers and clients get 403.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"create"},"app_source_id":{"type":"integer"}},"required":["action","app_source_id"],"title":"create","description":"`409 already_exists` when the source already has a key."}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true},"app_source_id":{"type":"integer"},"token":{"type":"string","description":"For POST /api/events; shown once."},"webhook_url":{"type":"string","format":"uri"}},"title":"create"}]}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/inbound":{"get":{"tags":["Sources and connections"],"operationId":"get_inbound","summary":"Inbound forms and bookings","description":"**Who:** Owner or member; viewers and clients get 403.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"project_id","in":"query","required":true,"schema":{"type":"integer"},"description":"Project id."}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"hooks":{"type":"array","items":{"type":"object"},"description":"id, provider, label, signed, received, last_at, url"},"providers":{"type":"object"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Sources and connections"],"operationId":"post_inbound","summary":"Create or remove an inbound address","description":"**Who:** Owner or member; viewers and clients get 403.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"create"},"project_id":{"type":"integer"},"provider":{"type":"string","enum":["typeform","calendly","gravityforms","overig","github","belmeting"]},"secret":{"type":"string","description":"To verify the signature; required for GitHub."}},"required":["action","project_id","provider"],"title":"create"},{"type":"object","properties":{"action":{"const":"delete"},"id":{"type":"integer"}},"required":["action","id"],"title":"delete"}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true}},"title":"create"},{"type":"object","properties":{"ok":{"const":true}},"title":"delete"}]}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/clarity":{"get":{"tags":["Sources and connections"],"operationId":"get_clarity","summary":"Microsoft Clarity: three-day summary","description":"Cached for three hours because Clarity allows ten requests per day. `422 setup_required` without a token, `502 clarity_unavailable` when Clarity fails.\n\n**Who:** Team; clients with access to the Website module.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"integration_id","in":"query","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"summary":{"type":"object","description":"sessions, users, rage_clicks"},"days":{"type":"integer"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/clarity/pages":{"get":{"tags":["Sources and connections"],"operationId":"get_clarity_pages","summary":"Clarity: friction per page over 28 days","description":"**Who:** Team; clients with access to the Website module.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"project_id","in":"query","required":true,"schema":{"type":"integer"},"description":"Project id."}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"pages":{"type":"array","items":{"type":"object"},"description":"url, sessions, dead, rage, quickback, error_clicks, script_errors, scroll, active_seconds, friction"},"days":{"type":"integer"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/feed":{"get":{"tags":["Sources and connections"],"operationId":"get_feed","summary":"Is the data feed on","description":"**Who:** Owner or member; viewers and clients get 403.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"project_id","in":"query","required":true,"schema":{"type":"integer"},"description":"Project id."}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"active":{"type":"boolean"},"created_at":{"type":["string","null"],"format":"date-time"},"last_used_at":{"type":["string","null"],"format":"date-time"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Sources and connections"],"operationId":"post_feed","summary":"Create or revoke the data feed (CSV)","description":"**Who:** Owner or member; viewers and clients get 403.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"create"},"project_id":{"type":"integer"}},"required":["action","project_id"],"title":"create","description":"A new feed invalidates the old address."},{"type":"object","properties":{"action":{"const":"revoke"},"project_id":{"type":"integer"}},"required":["action","project_id"],"title":"revoke"}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true},"url":{"type":"string","format":"uri"},"formula":{"type":"string","description":"For Google Sheets: =IMPORTDATA(\"…\")."}},"title":"create"},{"type":"object","properties":{"ok":{"const":true}},"title":"revoke"}]}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/data":{"get":{"tags":["Metrics"],"operationId":"get_data","summary":"GA4, Search Console and own systems","description":"`scope=overview`: totals and daily series per site. `realtime`: active users per property. `detail`: one property (`property=properties/123`). `project`: web, search and product for one project (`project=5`). `503 no_connections` without a working Google connection.\n\n**Who:** Everyone in the workspace; a client only sees their own projects.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"scope","in":"query","required":false,"schema":{"type":"string","enum":["overview","realtime","detail","project"]}},{"name":"property","in":"query","required":false,"schema":{"type":"string"}},{"name":"project","in":"query","required":false,"schema":{"type":"integer"}},{"name":"days","in":"query","required":false,"schema":{"type":"integer","enum":[7,28,90]},"description":"Period in days; defaults to 28."}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"generated":{"type":"integer"},"days":{"type":"integer"},"sites":{"type":"array","items":{"type":"object"}},"apps":{"type":"array","items":{"type":"object"}},"live":{"type":"object"}},"additionalProperties":true}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"},"502":{"$ref":"#/components/responses/502"},"503":{"$ref":"#/components/responses/503"}}}},"/api/landing":{"get":{"tags":["Metrics"],"operationId":"get_landing","summary":"Landing pages with conversion","description":"**Who:** Team; clients with access to the Website module.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"project_id","in":"query","required":true,"schema":{"type":"integer"},"description":"Project id."},{"name":"days","in":"query","required":false,"schema":{"type":"integer","enum":[7,28,90]},"description":"Period in days; defaults to 28."},{"name":"channel","in":"query","required":false,"schema":{"type":"string"},"description":"For example Organic Search."}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"pages":{"type":"array","items":{"type":"object"}},"days":{"type":"integer"},"channel":{"type":["string","null"]}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/revenue":{"get":{"tags":["Metrics"],"operationId":"get_revenue","summary":"Revenue, MRR, churn and payment success","description":"**Who:** Team; clients with approval rights on Overview (otherwise `hidden: true`).\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"project_id","in":"query","required":true,"schema":{"type":"integer"},"description":"Project id."}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"revenue":{"type":["object","null"]},"hidden":{"type":"boolean"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/attribution":{"get":{"tags":["Metrics"],"operationId":"get_attribution","summary":"Channel to payment, LTV and CAC","description":"**Who:** Team; clients with approval rights on Overview (otherwise `hidden: true`).\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"project_id","in":"query","required":true,"schema":{"type":"integer"},"description":"Project id."}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"attribution":{"type":["object","null"]},"hidden":{"type":"boolean"},"can_manage":{"type":"boolean"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Metrics"],"operationId":"post_attribution","summary":"Costs per channel per month","description":"**Who:** Owner or member; viewers and clients get 403.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"cost"},"project_id":{"type":"integer"},"month":{"type":"string","pattern":"^\\d{4}-(0[1-9]|1[0-2])$"},"channel":{"type":"string"},"amount":{"type":"number","description":"In euros; 0 removes the costs."},"note":{"type":"string"}},"required":["action","project_id","month","channel","amount"],"title":"cost"}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true}},"title":"cost"}]}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/brand":{"get":{"tags":["Metrics"],"operationId":"get_brand","summary":"Branded versus non-branded search","description":"**Who:** Team; clients with access to the Search module.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"project_id","in":"query","required":true,"schema":{"type":"integer"},"description":"Project id."},{"name":"days","in":"query","required":false,"schema":{"type":"integer","enum":[7,28,90]},"description":"Period in days; defaults to 28."}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"days":{"type":"integer"},"visibility":{"type":"array","items":{"type":"object"}}},"additionalProperties":true}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/cause":{"get":{"tags":["Metrics"],"operationId":"get_cause","summary":"Cause of a change","description":"Returns `disabled: true` when the feature is off for the workspace.\n\n**Who:** Team; clients with Website (or Search for clicks).\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"project_id","in":"query","required":true,"schema":{"type":"integer"},"description":"Project id."},{"name":"metric","in":"query","required":false,"schema":{"type":"string","enum":["sessions","key_events","clicks"]}},{"name":"days","in":"query","required":false,"schema":{"type":"integer","enum":[7,14,28,90]}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"cause":{"type":["object","null"]},"disabled":{"type":"boolean"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/cookieless":{"get":{"tags":["Metrics"],"operationId":"get_cookieless","summary":"Cookieless measurement over 28 days","description":"**Who:** Team; clients with access to the Website module.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"project_id","in":"query","required":true,"schema":{"type":"integer"},"description":"Project id."}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"days":{"type":"array","items":{"type":"object"},"description":"day, pageviews, visitors"},"pages":{"type":"array","items":{"type":"object"},"description":"label, pageviews, visitors"},"sources":{"type":"array","items":{"type":"object"},"description":"label, pageviews, visitors"},"refs":{"type":"array","items":{"type":"object"},"description":"label, pageviews, visitors; waarden van ?ref= of ?source= in het adres"},"referrers":{"type":"array","items":{"type":"object"},"description":"label (domein), pageviews, visitors"},"consent":{"type":"object","description":"alles, statistieken, advertenties, geweigerd → bezoekers (28 dagen)"},"ga4_users":{"type":["integer","null"],"description":"som van GA4-gebruikers per dag over dezelfde 28 dagen"},"totals":{"type":"object","description":"pageviews, visitors"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/vitals":{"get":{"tags":["Metrics"],"operationId":"get_vitals","summary":"Web Vitals from real visitors","description":"**Who:** Team; clients with access to the Website module.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"project_id","in":"query","required":true,"schema":{"type":"integer"},"description":"Project id."}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"has_script":{"type":"boolean"}},"additionalProperties":true}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/forms":{"get":{"tags":["Metrics"],"operationId":"get_forms","summary":"Form analytics, leads and cohorts","description":"**Who:** Team; clients with access to the Website module.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"project_id","in":"query","required":true,"schema":{"type":"integer"},"description":"Project id."},{"name":"days","in":"query","required":false,"schema":{"type":"integer"},"description":"7 to 90; defaults to 28."}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"forms":{"type":"array","items":{"type":"object"},"description":"page, form, starts, submits, rate, fields, worst"},"has_script":{"type":"boolean"},"leads":{"type":"object"},"cohorts":{"type":"object"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/speed":{"get":{"tags":["Metrics"],"operationId":"get_speed","summary":"PageSpeed per page: mobile and desktop, four categories, history and audits with tips","description":"**Who:** Team; clients with access to the Website module.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"project_id","in":"query","required":true,"schema":{"type":"integer"},"description":"Project id."}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"pages":{"type":"array","items":{"type":"object"},"description":"url, path, planned, mobile, desktop (scores, lab, field, origin_field, audits), mobile_history, desktop_history, mobile_error, desktop_error"},"summary":{"type":"object","description":"average, worst, opportunities, issues, origin_field"},"tips":{"type":"object","description":"Tip per audit id for the project's stack."},"quota":{"type":"object","description":"used, budget, blocked_until, source"},"measuring":{"type":"boolean"},"can_manage":{"type":"boolean"},"pages_limit":{"type":"integer"},"setup":{"type":["object","null"],"description":"Why nothing is measured (error), a link to fix it (enable_url) and fix: steps, links, copy."}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Metrics"],"operationId":"post_speed","summary":"Measure pages now with PageSpeed Insights","description":"**Who:** Owner or member; viewers and clients get 403.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"measure"},"project_id":{"type":"integer"},"url":{"type":"string","format":"uri","description":"Empty: all measured pages of the project."}},"required":["action","project_id"],"title":"measure","description":"Measures in the background, mobile and desktop; at most once every two minutes per project (429)."}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true},"pages":{"type":"integer"}},"title":"measure"}]}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/seo":{"get":{"tags":["Search and SEO"],"operationId":"get_seo","summary":"Query and meta worksheet","description":"**Who:** Team; clients with access to the Search module.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"project_id","in":"query","required":true,"schema":{"type":"integer"},"description":"Project id."},{"name":"days","in":"query","required":false,"schema":{"type":"integer","enum":[7,28,90]},"description":"Period in days; defaults to 28."}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"days":{"type":"integer"},"sites":{"type":"array","items":{"type":"object"}},"errors":{"type":"array","items":{"type":"object"}},"pages":{"type":"array","items":{"type":"object"},"description":"At most 300, with open recommendations and index status."},"page_count":{"type":"integer"},"ctr_curve":{"type":"array","items":{"type":"object"},"description":"position, ctr"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"},"502":{"$ref":"#/components/responses/502"}}}},"/api/keywords":{"get":{"tags":["Search and SEO"],"operationId":"get_keywords","summary":"Tracked keywords","description":"**Who:** Team; clients with access to the Search module.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"project_id","in":"query","required":true,"schema":{"type":"integer"},"description":"Project id."}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"keywords":{"type":"array","items":{"type":"object"}},"suggestions":{"type":"array","items":{"type":"object"}},"can_manage":{"type":"boolean"},"ads_candidates":{"type":"array","items":{"type":"object"}},"clusters":{"type":"array","items":{"type":"object"}},"seasons":{"type":"object"},"title_tests":{"type":"array","items":{"type":"object"}}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Search and SEO"],"operationId":"post_keywords","summary":"Add, target or remove keywords","description":"**Who:** Owner or member (manages Search); others get 404.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"add"},"project_id":{"type":"integer"},"keywords":{"type":"string","description":"One per line or comma-separated; at most 200 per project."}},"required":["action","project_id","keywords"],"title":"add"},{"type":"object","properties":{"action":{"const":"target"},"project_id":{"type":"integer"},"id":{"type":"integer"},"target_url":{"type":"string","format":"uri"}},"required":["action","project_id","id"],"title":"target"},{"type":"object","properties":{"action":{"const":"delete"},"project_id":{"type":"integer"},"id":{"type":"integer"}},"required":["action","project_id","id"],"title":"delete"}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true},"keywords":{"type":"array","items":{"type":"object"}},"suggestions":{"type":"array","items":{"type":"object"}}},"title":"add"},{"type":"object","properties":{"ok":{"const":true},"keywords":{"type":"array","items":{"type":"object"}},"suggestions":{"type":"array","items":{"type":"object"}}},"title":"target"},{"type":"object","properties":{"ok":{"const":true},"keywords":{"type":"array","items":{"type":"object"}},"suggestions":{"type":"array","items":{"type":"object"}}},"title":"delete"}]}}}},"401":{"$ref":"#/components/responses/401"},"402":{"$ref":"#/components/responses/402"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/competition":{"get":{"tags":["Search and SEO"],"operationId":"get_competition","summary":"Competitors, content gaps, backlinks and map rankings","description":"`source` says which source is active: DataForSEO or the free sources (Bing, Brave, own crawler).\n\n**Who:** Team; clients with access to the Search module.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"project_id","in":"query","required":true,"schema":{"type":"integer"},"description":"Project id."}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"enabled":{"type":"boolean"},"source":{"type":"string"},"free":{"type":"object"},"backlinks_source":{"type":["string","null"]},"gaps_source":{"type":"string"},"competitors":{"type":"array","items":{"type":"string"}},"keywords":{"type":"array","items":{"type":"object"}},"gaps":{"type":"array","items":{"type":"object"}},"backlinks":{"type":["object","null"]},"grid":{"type":"object"},"spent":{"type":["number","null"]},"can_manage":{"type":"boolean"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Search and SEO"],"operationId":"post_competition","summary":"Set competitors and the map grid","description":"**Who:** Owner or member (manages Search); others get 404.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"add"},"project_id":{"type":"integer"},"domain":{"type":"string","description":"At most five per project."}},"required":["action","project_id","domain"],"title":"add"},{"type":"object","properties":{"action":{"const":"remove"},"project_id":{"type":"integer"},"domain":{"type":"string"}},"required":["action","project_id","domain"],"title":"remove"},{"type":"object","properties":{"action":{"const":"grid"},"project_id":{"type":"integer"},"business_name":{"type":"string"},"keyword":{"type":"string"},"lat":{"type":"number"},"lng":{"type":"number"},"radius_km":{"type":"number","description":"0.5 to 25; defaults to 3."}},"required":["action","project_id","business_name","keyword","lat","lng"],"title":"grid"},{"type":"object","properties":{"action":{"const":"brave"},"project_id":{"type":"integer"}},"required":["action","project_id"],"title":"brave","description":"Brave Search positions, live and not stored; at most once per ten minutes per project (`429 too_many`)."}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true}},"title":"add"},{"type":"object","properties":{"ok":{"const":true}},"title":"remove"},{"type":"object","properties":{"ok":{"const":true}},"title":"grid"},{"type":"object","properties":{"ok":{"const":true},"live":{"type":"array","items":{"type":"object"}}},"title":"brave"}]}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/audit":{"get":{"tags":["Search and SEO"],"operationId":"get_audit","summary":"Site audit from the latest crawl","description":"**Who:** Team; clients with access to the Search module.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"project_id","in":"query","required":true,"schema":{"type":"integer"},"description":"Project id."}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"crawled_at":{"type":["string","null"],"format":"date-time"},"pages":{"type":"integer"},"working":{"type":"integer"},"redirects":{"type":"integer"},"errors":{"type":"integer"},"links":{"type":"integer"},"issues":{"type":"array","items":{"type":"object"}},"suggestions":{"type":"array","items":{"type":"object"}},"can_crawl":{"type":"boolean"},"llms_txt":{"type":["object","null"]},"budget":{"type":["object","null"]},"indexnow":{"type":"object","description":"key, verified_at"},"text":{"type":["object","null"]}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Search and SEO"],"operationId":"post_audit","summary":"Start a crawl or set up IndexNow","description":"**Who:** Owner or member (manages Search); others get 404.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"crawl"},"project_id":{"type":"integer"}},"required":["action","project_id"],"title":"crawl"},{"type":"object","properties":{"action":{"const":"indexnow"},"project_id":{"type":"integer"}},"required":["action","project_id"],"title":"indexnow"}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true},"task_id":{"type":"string","description":"Follow the task with GET /api/task?id=…"}},"title":"crawl"},{"type":"object","properties":{"ok":{"const":true},"key":{"type":"string"},"location":{"type":"string","format":"uri"},"verified":{"type":"boolean"}},"title":"indexnow"}]}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/schema":{"get":{"tags":["Search and SEO"],"operationId":"get_schema","summary":"Schema wizard: types and what is known","description":"**Who:** Team; clients with access to the Search module.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"project_id","in":"query","required":true,"schema":{"type":"integer"},"description":"Project id."}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"types":{"type":"object"},"prefill":{"type":"object","description":"name, url"},"present":{"type":"array","items":{"type":"string"}}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Search and SEO"],"operationId":"post_schema","summary":"Build JSON-LD","description":"No `action`. Returns the JSON-LD, or which fields are missing.\n\n**Who:** Team; clients with access to the Search module.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"project_id":{"type":"integer"},"type":{"type":"string"},"fields":{"type":"object"}},"required":["project_id","type"]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true}},"additionalProperties":true}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/logs":{"get":{"tags":["Search and SEO"],"operationId":"get_logs","summary":"Analysis of the latest log file","description":"**Who:** Team; clients with access to the Search module.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"project_id","in":"query","required":true,"schema":{"type":"integer"},"description":"Project id."}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"report":{"type":["object","null"]},"can_upload":{"type":"boolean"},"from_server":{"type":"boolean"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Search and SEO"],"operationId":"post_logs","summary":"Upload an access log","description":"Multipart, no `action`. Combined format, .gz allowed, at most 100 MB.\n\n**Who:** Owner or member (manages Search); others get 404.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"project_id":{"type":"integer"},"file":{"type":"string","format":"binary"}},"required":["project_id","file"]}}}},"responses":{"201":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"lines":{"type":"integer"},"bot_lines":{"type":"integer"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/content":{"get":{"tags":["Search and SEO"],"operationId":"get_content","summary":"Content calendar","description":"**Who:** Team; clients with access to the Search module.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"project_id","in":"query","required":true,"schema":{"type":"integer"},"description":"Project id."}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"items":{"type":"array","items":{"type":"object"}},"suggestions":{"type":"array","items":{"type":"object"}},"can_manage":{"type":"boolean"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Search and SEO"],"operationId":"post_content","summary":"Plan content, newsletter and socials","description":"**Who:** Owner or member (manages Search); others get 404.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"add"},"project_id":{"type":"integer"},"title":{"type":"string"},"query":{"type":"string"},"publish_on":{"type":"string","format":"date"},"reason":{"type":"string"},"url":{"type":"string","format":"uri"}},"required":["action","project_id","title"],"title":"add"},{"type":"object","properties":{"action":{"const":"update"},"id":{"type":"integer"},"title":{"type":"string"},"query":{"type":"string"},"publish_on":{"type":["string","null"],"format":"date"},"status":{"type":"string","enum":["idee","schrijven","klaar","live"]},"url":{"type":"string","format":"uri"},"reason":{"type":"string"}},"required":["action","id"],"title":"update","description":"Setting `live` adds it to the timeline."},{"type":"object","properties":{"action":{"const":"delete"},"id":{"type":"integer"}},"required":["action","id"],"title":"delete"},{"type":"object","properties":{"action":{"const":"newsletter"},"project_id":{"type":"integer"},"days":{"type":"integer"},"intro":{"type":"string"},"skip":{"type":"array","items":{"type":"string"}}},"required":["action","project_id"],"title":"newsletter"},{"type":"object","properties":{"action":{"const":"newsletter_draft"},"project_id":{"type":"integer"},"target":{"type":"string","enum":["mailchimp","brevo"]},"subject":{"type":"string"},"list_id":{"type":"string"},"from_name":{"type":"string"},"reply_to":{"type":"string","format":"email"},"days":{"type":"integer"},"intro":{"type":"string"},"skip":{"type":"array","items":{"type":"string"}}},"required":["action","project_id","target","subject","list_id","from_name","reply_to"],"title":"newsletter_draft"},{"type":"object","properties":{"action":{"const":"social_drafts"},"project_id":{"type":"integer"},"skip":{"type":"array","items":{"type":"string"}}},"required":["action","project_id"],"title":"social_drafts","description":"Creates drafts in socials.instnt.studio."}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true},"id":{"type":"integer"}},"title":"add"},{"type":"object","properties":{"ok":{"const":true}},"title":"update"},{"type":"object","properties":{"ok":{"const":true}},"title":"delete"},{"type":"object","properties":{"ok":{"const":true},"items":{"type":"array","items":{"type":"object"}},"html":{"type":"string"}},"title":"newsletter"},{"type":"object","properties":{"ok":{"const":true},"draft_id":{"type":"string"}},"title":"newsletter_draft"},{"type":"object","properties":{"ok":{"const":true}},"title":"social_drafts"}]}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/writeback":{"post":{"tags":["Search and SEO"],"operationId":"post_writeback","summary":"Write-back key for WordPress or Drupal","description":"**Who:** Owner or member (manages Search); others get 404.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"token"},"project_id":{"type":"integer"}},"required":["action","project_id"],"title":"token"}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true},"token":{"type":"string","description":"wb_…; shown once, replaces the previous one."}},"title":"token"}]}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/advice":{"get":{"tags":["Advice, questions and AI"],"operationId":"get_advice","summary":"Recommendations for a project","description":"With `scope=workspace` (team only) the ten most important actions across projects. Each item has `why`: the parts of its ranking (clicks, confidence, effort, what the team learned).\n\n**Who:** Team; clients with access to the Advice module.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"project_id","in":"query","required":true,"schema":{"type":"integer"},"description":"Project id."},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["open","planned","done","bevestiging","dismissed","expired","all","actief","archief"]},"description":"Defaults to open. `bevestiging`: done, but not yet confirmed by the source. `actief`: open and planned. `archief`: done, dismissed and expired, newest first."},{"name":"group","in":"query","required":false,"schema":{"type":"string","enum":["page"]},"description":"With `status=actief`: also `pages`, the recommendations per page."},{"name":"scope","in":"query","required":false,"schema":{"type":"string","enum":["workspace"]}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"items":{"type":"array","items":{"type":"object"},"description":"At most 500, most important first, with impact_euros and confirmation (state confirmed or waiting, by, at, waits_on, overdue, note)."},"euro_basis":{"type":["object","null"]},"counts":{"type":"object"},"tip":{"type":["object","null"]},"last_refresh":{"type":["string","null"],"format":"date-time"},"thresholds":{"type":"object"},"learned":{"type":"array","items":{"type":"object"}},"effect_days":{"type":"integer"},"can_manage":{"type":"boolean"},"has_github":{"type":"boolean"},"progress":{"type":"array","items":{"type":"object"},"description":"Per category: category, done, total (excluding expired)."},"effect":{"type":["object","null"],"description":"Only with `archief`: done, measured, clicks, impressions (after minus before, over measured recommendations)."},"pages":{"type":["array","null"],"items":{"type":"object"},"description":"Only with `status=actief&group=page`: url, path, ids, count, impact_clicks, categories."}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Advice, questions and AI"],"operationId":"post_advice","summary":"Status, assign, GitHub issue, recheck, make prompt","description":"**Who:** Owner or member; viewers and clients get 403.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"status"},"id":{"type":"integer"},"status":{"type":"string","enum":["open","planned","done","dismissed"]},"note":{"type":"string"},"task_url":{"type":"string","format":"uri"}},"required":["action","id","status"],"title":"status","description":"On `done` Studio records the current figures to measure the effect after 14 days, and adds it to the timeline. The advice then waits for confirmation by its source (Google, live check, lockfile, measurement)."},{"type":"object","properties":{"action":{"const":"assign"},"id":{"type":"integer"},"assignee_id":{"type":["integer","null"]},"due_date":{"type":["string","null"],"format":"date"}},"required":["action","id"],"title":"assign","description":"Assigning plans an open recommendation."},{"type":"object","properties":{"action":{"const":"issue"},"id":{"type":"integer"}},"required":["action","id"],"title":"issue","description":"Creates an issue in the project's GitHub repo (`502 github` when GitHub refuses)."},{"type":"object","properties":{"action":{"const":"recheck"},"id":{"type":"integer"}},"required":["action","id"],"title":"recheck"},{"type":"object","properties":{"action":{"const":"refresh"},"project_id":{"type":"integer"}},"required":["action","project_id"],"title":"refresh","description":"Re-runs all rules."},{"type":"object","properties":{"action":{"const":"prompt"},"project_id":{"type":"integer"},"ids":{"type":"array","items":{"type":"integer"},"description":"At most 30; only recommendations with `fixable: true` count."},"mode":{"type":"string","enum":["claude","algemeen"]},"mark":{"type":"boolean","description":"Defaults to true: sets the items to planned with a note."}},"required":["action","project_id","ids"],"title":"prompt","description":"Team only. Turns recommendations that can be fixed in code or configuration into one task, with evidence, definition of done and how to verify. URLs without query string; no personal data or keys."}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true},"item":{"type":"object"}},"title":"status"},{"type":"object","properties":{"ok":{"const":true},"item":{"type":"object"}},"title":"assign"},{"type":"object","properties":{"ok":{"const":true},"url":{"type":"string","format":"uri"}},"title":"issue"},{"type":"object","properties":{"ok":{"const":true},"task_id":{"type":"string","description":"Follow the task with GET /api/task?id=…"}},"title":"recheck"},{"type":"object","properties":{"ok":{"const":true},"task_id":{"type":"string","description":"Follow the task with GET /api/task?id=…"}},"title":"refresh"},{"type":"object","properties":{"ok":{"const":true},"prompt":{"type":"string","description":"Markdown, ready to paste into an AI session."},"filename":{"type":"string"},"items":{"type":"array","items":{"type":"integer"}}},"title":"prompt"}]}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/playbooks":{"get":{"tags":["Advice, questions and AI"],"operationId":"get_playbooks","summary":"Project playbooks","description":"**Who:** Team; clients with access to the Advice module.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"project_id","in":"query","required":true,"schema":{"type":"integer"},"description":"Project id."}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"runs":{"type":"array","items":{"type":"object"}},"available":{"type":"object"},"can_manage":{"type":"boolean"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Advice, questions and AI"],"operationId":"post_playbooks","summary":"Start and tick off a playbook","description":"**Who:** Owner or member (manages Advice); others get 404.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"start"},"project_id":{"type":"integer"},"playbook":{"type":"string"}},"required":["action","project_id","playbook"],"title":"start"},{"type":"object","properties":{"action":{"const":"step"},"id":{"type":"integer"},"step":{"type":"string"},"done":{"type":"boolean"}},"required":["action","id","step"],"title":"step"},{"type":"object","properties":{"action":{"const":"finish"},"id":{"type":"integer"}},"required":["action","id"],"title":"finish"}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true},"id":{"type":"integer"}},"title":"start"},{"type":"object","properties":{"ok":{"const":true}},"title":"step"},{"type":"object","properties":{"ok":{"const":true}},"title":"finish"}]}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/experiments":{"get":{"tags":["Advice, questions and AI"],"operationId":"get_experiments","summary":"Experiment log and hypothesis cards","description":"**Who:** Team; clients with access to the Advice module.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"project_id","in":"query","required":true,"schema":{"type":"integer"},"description":"Project id."}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"experiments":{"type":"array","items":{"type":"object"}},"cards":{"type":"array","items":{"type":"object"}},"can_manage":{"type":"boolean"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Advice, questions and AI"],"operationId":"post_experiments","summary":"Record or delete an experiment","description":"**Who:** Owner or member (manages Advice); others get 404.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"save"},"project_id":{"type":"integer"},"id":{"type":"integer","description":"Without id: new."},"title":{"type":"string"},"hypothesis":{"type":"string"},"metric":{"type":"string"},"page_url":{"type":"string","format":"uri"},"starts_on":{"type":"string","format":"date"},"ends_on":{"type":"string","format":"date"},"outcome":{"type":"string","enum":["won","lost","neutral"]},"result":{"type":"string"}},"required":["action","project_id","title"],"title":"save"},{"type":"object","properties":{"action":{"const":"delete"},"project_id":{"type":"integer"},"id":{"type":"integer"}},"required":["action","project_id","id"],"title":"delete"}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true},"id":{"type":"integer"}},"title":"save"},{"type":"object","properties":{"ok":{"const":true}},"title":"delete"}]}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/questions":{"get":{"tags":["Advice, questions and AI"],"operationId":"get_questions","summary":"Questions about a report, recommendation or project","description":"Returns `disabled: true` when questions are off for the workspace.\n\n**Who:** Team and clients with access to the subject; `open=1` team only.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"project_id","in":"query","required":false,"schema":{"type":"integer"}},{"name":"subject_type","in":"query","required":false,"schema":{"type":"string","enum":["report","advice","project"]}},{"name":"subject_id","in":"query","required":false,"schema":{"type":"integer"}},{"name":"open","in":"query","required":false,"schema":{"type":"boolean"},"description":"All open questions across clients."}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"questions":{"type":"array","items":{"type":"object"},"description":"id, label, project_id, project_name, subject_type, subject_id, subject_title, anchor, status (open|wacht_ons|wacht_klant|opgelost|gesloten), link, created_by, updated_at, messages (body, from_team, internal, name, at)"},"disabled":{"type":"boolean"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Advice, questions and AI"],"operationId":"post_questions","summary":"Ask, reply or close a question","description":"**Who:** Team and clients with access to the subject.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"ask"},"project_id":{"type":"integer"},"subject_type":{"type":"string","enum":["report","advice","project"]},"subject_id":{"type":"integer"},"anchor":{"type":"string"},"body":{"type":"string","description":"At most 4000 characters."}},"required":["action","project_id","subject_type","subject_id","body"],"title":"ask"},{"type":"object","properties":{"action":{"const":"reply"},"question_id":{"type":"integer"},"body":{"type":"string"}},"required":["action","question_id","body"],"title":"reply"},{"type":"object","properties":{"action":{"const":"close"},"question_id":{"type":"integer"}},"required":["action","question_id"],"title":"close","description":"The team, or whoever asked."}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true},"question_id":{"type":"integer"}},"title":"ask"},{"type":"object","properties":{"ok":{"const":true}},"title":"reply"},{"type":"object","properties":{"ok":{"const":true}},"title":"close"}]}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/support":{"get":{"tags":["Advice, questions and AI"],"operationId":"get_support","summary":"Support: tickets","description":"Client questions to the agency (`deel=klant`, Support module, from Copiloot) and questions to instnt (`deel=instnt`). A question about a report or recommendation is a ticket too. Without `id` a list with counts per status; with `id` one ticket with the conversation, attachments and, for the handler, project context, internal notes, the team and saved replies. A ticket of another workspace returns 404.\n\n**Who:** Client: own tickets (with the Support module). Team: all client tickets; viewers read only. `deel=instnt`: owner or member. `admin=1`: platform admins only.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"deel","in":"query","required":false,"schema":{"type":"string","enum":["klant","instnt"]}},{"name":"admin","in":"query","required":false,"schema":{"type":"boolean"}},{"name":"id","in":"query","required":false,"schema":{"type":"integer"}},{"name":"sets","in":"query","required":false,"schema":{"type":"boolean"},"description":"Only the sets of saved replies to load (team, 13-10-2026)."},{"name":"lang","in":"query","required":false,"schema":{"type":"string","enum":["nl","en"]}},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["actief","te_laat","open","wacht_ons","wacht_klant","opgelost","gesloten"]},"description":"Defaults to actief: everything except resolved and closed. Empty for all."},{"name":"q","in":"query","required":false,"schema":{"type":"string"},"description":"Searches subject, number and messages."},{"name":"project_id","in":"query","required":false,"schema":{"type":"integer"}},{"name":"mine","in":"query","required":false,"schema":{"type":"boolean"},"description":"Only assigned to me."}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"tickets":{"type":"array","items":{"type":"object"},"description":"id, label, number, scope, title, subject_type, anchor, project, requester, status, priority, channel, assigned, due_at, overdue, messages, last_from_team, created_at, updated_at, workspace"},"counts":{"type":"object"},"overdue":{"type":"integer"},"module":{"type":"object"},"statuses":{"type":"object"},"priorities":{"type":"object"},"can_create":{"type":"boolean"},"sets":{"type":"array","items":{"type":"object"},"description":"With sets=1: key, label, description, count, titles."},"template_sets":{"type":"array","items":{"type":"object"},"description":"For the team: the same sets."},"settings":{"type":"object","description":"Team only: sla, sla_default, inbound_available, inbound_on, address, can_manage."},"ticket":{"type":"object","description":"With id: the ticket plus thread, link, role, can, and for the handler context, team, templates and projects."}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Advice, questions and AI"],"operationId":"post_support","summary":"Support: ask and handle questions","description":"Attachments as multipart `files[]`: at most 5 per message and 10 MB per file; images (no SVG), pdf, text, csv and Office files. The type follows from the content, not the name.\n\n**Who:** Per action: a client creates and answers their own tickets; owner and member handle and configure; with `admin: true` platform admins handle questions to instnt.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"create"},"deel":{"type":"string","enum":["klant","instnt"]},"title":{"type":"string","description":"At most 160 characters."},"body":{"type":"string","description":"At most 10,000 characters."},"project_id":{"type":"integer","description":"Client with more than one project: required."},"priority":{"type":"string","enum":["laag","normaal","hoog","urgent"],"description":"Only with deel=instnt."},"files":{"type":"string","format":"binary"}},"required":["action","deel","title","body"],"title":"create"},{"type":"object","properties":{"action":{"const":"reply"},"id":{"type":"integer"},"template_id":{"type":"integer","description":"The saved reply this came from; if it has actions (macro, 13-10-2026), Studio runs them after sending."},"body":{"type":"string"},"files":{"type":"string","format":"binary"},"admin":{"type":"boolean"}},"required":["action","id","body"],"title":"reply","description":"On a closed ticket only the handler."},{"type":"object","properties":{"action":{"const":"note"},"id":{"type":"integer"},"body":{"type":"string"},"files":{"type":"string","format":"binary"},"admin":{"type":"boolean"}},"required":["action","id","body"],"title":"note","description":"Internal note: the requester never sees it, nor its attachments."},{"type":"object","properties":{"action":{"const":"status"},"id":{"type":"integer"},"status":{"type":"string","enum":["open","wacht_ons","wacht_klant","opgelost","gesloten"]},"admin":{"type":"boolean"}},"required":["action","id","status"],"title":"status","description":"The requester can only close."},{"type":"object","properties":{"action":{"const":"assign"},"id":{"type":"integer"},"user_id":{"type":["integer","null"]},"admin":{"type":"boolean"}},"required":["action","id"],"title":"assign"},{"type":"object","properties":{"action":{"const":"priority"},"id":{"type":"integer"},"priority":{"type":"string","enum":["laag","normaal","hoog","urgent"]},"admin":{"type":"boolean"}},"required":["action","id","priority"],"title":"priority","description":"Recalculates a running deadline."},{"type":"object","properties":{"action":{"const":"project"},"id":{"type":"integer"},"project_id":{"type":["integer","null"]},"admin":{"type":"boolean"}},"required":["action","id"],"title":"project"},{"type":"object","properties":{"action":{"const":"settings"},"sla":{"type":"object","description":"Hours per priority (laag, normaal, hoog, urgent), each 1–720."},"inbound_on":{"type":"boolean"}},"required":["action"],"title":"settings"},{"type":"object","properties":{"action":{"const":"rotate_address"}},"required":["action"],"title":"rotate_address","description":"New address for mail to support; the old one stops working at once."},{"type":"object","properties":{"action":{"const":"template_save"},"id":{"type":"integer"},"project_id":{"type":["integer","null"],"description":"Only for this project (13-10-2026); empty means all projects. At most 50 per project and 50 general."},"category":{"type":"string","description":"Category, at most 40 characters (13-10-2026)."},"actions":{"type":"object","description":"Macro (13-10-2026): status (wacht|afgesloten), tags (at most five), assign_me; applied after sending in chat."},"title":{"type":"string"},"shortcut":{"type":"string","description":"Shortcut for chat, without the slash: lowercase letters, digits and hyphens, at most 30."},"body":{"type":"string"},"buttons":{"type":"array","items":{"type":"string"},"description":"Buttons in chat, at most four (22-10-2026)."},"admin":{"type":"boolean"}},"required":["action","title","body"],"title":"template_save","description":"The same saved replies apply to Support and to chat."},{"type":"object","properties":{"action":{"const":"template_delete"},"id":{"type":"integer"},"admin":{"type":"boolean"}},"required":["action","id"],"title":"template_delete"},{"type":"object","properties":{"action":{"const":"template_import"},"set":{"type":"string","enum":["basis","webshop","diensten","horeca","software"]},"project_id":{"type":"integer","description":"Into this project instead of general."},"sets":{"type":"array","items":{"type":"string"},"description":"Several sets at once (13-10-2026)."},"only":{"type":"array","items":{"type":"string"},"description":"Only these replies, by title."},"lang":{"type":"string","enum":["nl","en"]}},"required":["action"],"title":"template_import","description":"Load a set of saved replies (13-10-2026). Adds without overwriting: the same name or shortcut is skipped, it stops above 50. Owner or member."}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true},"ticket":{"type":"object"}},"title":"create"},{"type":"object","properties":{"ok":{"const":true},"ticket":{"type":"object"}},"title":"reply"},{"type":"object","properties":{"ok":{"const":true},"ticket":{"type":"object"}},"title":"note"},{"type":"object","properties":{"ok":{"const":true},"ticket":{"type":"object"}},"title":"status"},{"type":"object","properties":{"ok":{"const":true},"ticket":{"type":"object"}},"title":"assign"},{"type":"object","properties":{"ok":{"const":true},"ticket":{"type":"object"}},"title":"priority"},{"type":"object","properties":{"ok":{"const":true},"ticket":{"type":"object"}},"title":"project"},{"type":"object","properties":{"ok":{"const":true},"settings":{"type":"object"}},"title":"settings"},{"type":"object","properties":{"ok":{"const":true},"settings":{"type":"object"}},"title":"rotate_address"},{"type":"object","properties":{"ok":{"const":true},"templates":{"type":"array","items":{"type":"object"}}},"title":"template_save"},{"type":"object","properties":{"ok":{"const":true},"templates":{"type":"array","items":{"type":"object"}}},"title":"template_delete"},{"type":"object","properties":{"ok":{"const":true},"templates":{"type":"array","items":{"type":"object"}},"imported":{"type":"object","description":"added, skipped, full"}},"title":"template_import"}]}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/support/attachment":{"get":{"tags":["Advice, questions and AI"],"operationId":"get_support_attachment","summary":"Ticket attachment","description":"Always as a download, with `X-Content-Type-Options: nosniff`.\n\n**Who:** Whoever may see the ticket; an attachment on an internal note only the handler.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"id","in":"query","required":true,"schema":{"type":"integer"}},{"name":"admin","in":"query","required":false,"schema":{"type":"boolean"}}],"responses":{"200":{"description":"Success.","content":{"application/octet-stream":{"schema":{"type":"string","format":"binary"}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/livechat":{"get":{"tags":["Advice, questions and AI"],"operationId":"get_livechat","summary":"Chat: conversations with visitors","description":"The inbox for the on-site chat (Chat module, from Copiloot). Without `id` a list with counts; with `id` one conversation with messages, internal notes, visitor context (page, language, device, earlier conversations), the team and saved replies with variables filled in. Opening a conversation marks it read. With `since` only messages after that id, for polling. Opening the inbox counts as online for the widget for ninety seconds. A conversation of another project or workspace returns 404.\n\n**Who:** Team: conversations of projects with the Chat module; viewers read only. Client: only for a project where they have Website on approve. `settings`: owner or member.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"view","in":"query","required":false,"schema":{"type":"string","enum":["open","wachtend","mijn","niet_toegewezen","buiten_tijden","afgesloten","alle"]},"description":"Defaults to open: everything not closed. wachtend: the team is up."},{"name":"project_id","in":"query","required":false,"schema":{"type":"integer"}},{"name":"label","in":"query","required":false,"schema":{"type":"string"},"description":"Only conversations with this label."},{"name":"sort","in":"query","required":false,"schema":{"type":"string","enum":["wachtend","nieuwste","activiteit"]},"description":"Defaults to wachtend: whoever has waited longest on the team first."},{"name":"q","in":"query","required":false,"schema":{"type":"string"},"description":"Searches name, email and messages."},{"name":"id","in":"query","required":false,"schema":{"type":"integer"}},{"name":"since","in":"query","required":false,"schema":{"type":"integer"}},{"name":"teller","in":"query","required":false,"schema":{"type":"boolean"},"description":"Counts only, for the navigation and the app icon."},{"name":"settings","in":"query","required":false,"schema":{"type":"integer"},"description":"Project id: the chat settings and the install snippet."}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"conversations":{"type":"array","items":{"type":"object"},"description":"id, project, name, email, status, offline, assigned, last, unread, tags, page_url, visitor_online, rating, ticket_id, created_at, last_message_at, waiting_since"},"counts":{"type":"object","description":"open, mijn, niet_toegewezen, wachtend, ongelezen; without teller also buiten_tijden, afgesloten, oudste_wacht, per_project (per project id: open, wachtend) and labels (the five most used: label, count); with teller=1 also projects (number of sites with chat for this user)."},"projects":{"type":"array","items":{"type":"object"},"description":"id, name, enabled, can_reply, notify_after"},"sort":{"type":"string"},"module":{"type":"object"},"can_manage":{"type":"boolean"},"statuses":{"type":"object"},"conversation":{"type":"object","description":"With id: the conversation plus messages, visitor, visitor_typing, rating_comment, ticket, can, team, templates and me (your own user id). Since 11-10-2026 visitor also has route (path, title, at: the pages of this visit, without query string, at most twenty, removed after 30 days without a message), read_upto and read_at (up to which message the visitor saw the window); a message can have faq (id, title). Since 12-10-2026 visitor.previous (id, status, created_at, last_at, rating, ticket, preview: the first sentence) also holds conversations from other browsers of the visitor, linked by code or StudioChat.identify(). Since 13-10-2026 viewers (id, name, typing): colleagues who also have this conversation open."},"settings":{"type":"object","description":"With settings: enabled, greeting, offline_message, color, position, hours, allowed_origins, allowed_hosts, prechat, proactive, notify_after, notify_users, snippet, token. Messages have `files` (attachments with a URL valid for half an hour); visitor has `consent_at`."}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Advice, questions and AI"],"operationId":"post_livechat","summary":"Chat: reply and configure","description":"**Who:** Whoever may reply: owner, member, or a client with Website on approve. Settings and deleting: owner or member.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"reply"},"id":{"type":"integer"},"faq_id":{"type":"integer","description":"A published FAQ article of this project as a card under the message (11-10-2026); body may then be empty."},"buttons":{"type":"array","items":{"type":"string"},"description":"At most four buttons of 1 to 40 characters under the message (22-10-2026); as a JSON string in multipart. The visitor picks one."},"file":{"type":"string","format":"binary","description":"Attachment (21-10-2026): image (jpg, png, webp, gif) or PDF, at most 10 MB; the content decides the type. Then as multipart/form-data; body may be empty. Also with note."},"body":{"type":"string","description":"At most 2,000 characters. {naam}, {voornaam}, {project}, {bureau} and {medewerker} are filled in."}},"required":["action","id","body"],"title":"reply","description":"Replying to an unassigned conversation assigns it to you. Not on a conversation that became a ticket."},{"type":"object","properties":{"action":{"const":"note"},"id":{"type":"integer"},"body":{"type":"string"}},"required":["action","id","body"],"title":"note","description":"Internal note: the visitor never sees it."},{"type":"object","properties":{"action":{"const":"status"},"id":{"type":"integer"},"status":{"type":"string","enum":["open","wacht","afgesloten"]}},"required":["action","id","status"],"title":"status"},{"type":"object","properties":{"action":{"const":"assign"},"id":{"type":"integer"},"user_id":{"type":["integer","null"]}},"required":["action","id"],"title":"assign"},{"type":"object","properties":{"action":{"const":"tags"},"id":{"type":"integer"},"tags":{"type":"array","items":{"type":"string"},"description":"At most ten, each at most 30 characters."}},"required":["action","id","tags"],"title":"tags"},{"type":"object","properties":{"action":{"const":"files"},"id":{"type":"integer"},"allow":{"type":"boolean"},"limit":{"type":["integer","null"],"description":"How many attachments the visitor may send before it closes again (13-10-2026); without it the site setting applies."}},"required":["action","id","allow"],"title":"files","description":"Allow or disallow attachments from the visitor in this conversation. Off by default; the visitor gets a system message and only then sees the paperclip."},{"type":"object","properties":{"action":{"const":"ticket"},"id":{"type":"integer"}},"required":["action","id"],"title":"ticket","description":"Converts the conversation to a Support ticket (module required) and closes it. With the visitor's email address, the reply goes by email."},{"type":"object","properties":{"action":{"const":"typing"},"id":{"type":"integer"}},"required":["action","id"],"title":"typing","description":"The visitor sees \"typing…\" for eight seconds."},{"type":"object","properties":{"action":{"const":"read"},"id":{"type":"integer"}},"required":["action","id"],"title":"read"},{"type":"object","properties":{"action":{"const":"activity"},"id":{"type":"integer"},"token":{"type":"string","description":"Push token of the Live Activity (hex)."},"environment":{"type":"string","enum":["sandbox","production"]},"end":{"type":"boolean","description":"Remove the token: the app no longer follows this conversation."}},"required":["action","id","token"],"title":"activity","description":"Live Activity on the iPhone (12-10-2026): only from an app session (otherwise 403 app_only). Studio updates the activity by push on a new message, a reply, assignment and closing; closing or a ticket ends it."},{"type":"object","properties":{"action":{"const":"delete"},"id":{"type":"integer"}},"required":["action","id"],"title":"delete","description":"Deletes the conversation with all messages, for example at the visitor's request."},{"type":"object","properties":{"action":{"const":"identity_secret"},"project_id":{"type":"integer"},"remove":{"type":"boolean"}},"required":["action","project_id"],"title":"identity_secret","description":"Create or renew the key for StudioChat.identify() (64 hex characters), or remove it with remove. Old signatures stop working."},{"type":"object","properties":{"action":{"const":"mute"},"id":{"type":"integer"},"muted":{"type":"boolean"}},"required":["action","id","muted"],"title":"mute","description":"Mute: no more push for this conversation, only for you (13-10-2026). The conversation returns muted."},{"type":"object","properties":{"action":{"const":"settings"},"project_id":{"type":"integer"},"enabled":{"type":"boolean"},"auto_close_minutes":{"type":["integer","null"],"description":"Close a conversation automatically after this many minutes without a visitor reply after the team answered (5 to 10080); empty means never (13-10-2026)."},"rating_aspects":{"type":"boolean","description":"Also let visitors rate friendliness, speed and expertise."},"file_types":{"type":"array","items":{"type":"string"},"description":"What a visitor may send (13-10-2026): afbeeldingen (images), pdf and/or documenten (documents)."},"file_max_mb":{"type":"integer","description":"1 to 10."},"file_limit":{"type":["integer","null"],"description":"After this many attachments, attachments close again in that conversation; empty means no limit."},"greeting":{"type":"string"},"offline_message":{"type":"string"},"color":{"type":["string","null"]},"position":{"type":"string","enum":["rechts","links"]},"greeting_buttons":{"type":"array","items":{"type":"string"},"description":"At most four buttons under the greeting; a click starts the conversation with that text."},"hide_branding":{"type":"boolean","description":"Window without instnt studio; only with white-label in the plan."},"hours":{"type":["object","null"],"description":"Per day (1 = Monday) a list of blocks [\"09:00\",\"17:00\"], in the workspace time zone; empty means always open."},"allowed_origins":{"type":"string","description":"Extra origins allowed to load the widget, besides the project domains. Over http only what is listed here."},"prechat":{"type":"object","description":"Form before the first message: when (nooit|offline|altijd), name/email/topic (uit|optioneel|verplicht), topics (at most ten), privacy (bool) and privacy_url (https). Outside opening hours email is always required. Replaces ask_email (21-10-2026)."},"proactive":{"type":"array","items":{"type":"object"},"description":"At most five: match (bevat|is), path, delay (0–600 s), message (at most 300 characters), every_days (0–365; 0 = once), only_new, only_open."},"notify_after":{"type":"integer","description":"Email after this many minutes without a reply, 1–240."},"notify_users":{"type":"array","items":{"type":"integer"}},"widget":{"type":"object","description":"The window (10-10-2026), per group; what is missing stays as it was. home: mode (zoeken|samen|chat), contact (altijd|na_zoeken), featured_count (0–8), categories, conversation_card, logo. contact: offline (bericht|faq), alternatives (at most three: type telefoon|email|whatsapp, value, label, text, when altijd|offline), links (at most three: label, https URL). faq: enabled, suggest, feedback, log_misses. team: names (voornaam|volledig|voornaam_foto|bureau), queue. visibility: paths (at most ten: match bevat|is, path starting with /), mobile, offline. launcher: offset_x, offset_y (0–200), label_mobile. texts: per language (nl, en) greeting, offline, home_title, home_intro, search, contact, reply_time, launcher."}},"required":["action","project_id"],"title":"settings"},{"type":"object","properties":{"action":{"const":"refused_allow"},"project_id":{"type":"integer"},"origin":{"type":"string"}},"required":["action","project_id","origin"],"title":"refused_allow","description":"Allow a refused origin: it is added to allowed_origins and leaves the list."},{"type":"object","properties":{"action":{"const":"refused_ignore"},"project_id":{"type":"integer"},"origin":{"type":"string"}},"required":["action","project_id","origin"],"title":"refused_ignore","description":"Hide a refused origin; it is removed after 7 days."},{"type":"object","properties":{"action":{"const":"rotate_token"},"project_id":{"type":"integer"}},"required":["action","project_id"],"title":"rotate_token","description":"New widget token; the old snippet stops working at once."}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true},"conversation":{"type":"object"}},"title":"reply"},{"type":"object","properties":{"ok":{"const":true},"conversation":{"type":"object"}},"title":"note"},{"type":"object","properties":{"ok":{"const":true},"conversation":{"type":"object"}},"title":"status"},{"type":"object","properties":{"ok":{"const":true},"conversation":{"type":"object"}},"title":"assign"},{"type":"object","properties":{"ok":{"const":true},"conversation":{"type":"object"}},"title":"tags"},{"type":"object","properties":{"ok":{"const":true},"conversation":{"type":"object"}},"title":"files"},{"type":"object","properties":{"ok":{"const":true},"conversation":{"type":"object"}},"title":"ticket"},{"type":"object","properties":{"ok":{"const":true}},"title":"typing"},{"type":"object","properties":{"ok":{"const":true}},"title":"read"},{"type":"object","properties":{"ok":{"const":true},"state":{"type":"object","description":"status, lastMessage, lastFromVisitor, lastAt, waitingSince, assigned (tijden in Unix-seconden)"}},"title":"activity"},{"type":"object","properties":{"ok":{"const":true}},"title":"delete"},{"type":"object","properties":{"ok":{"const":true},"settings":{"type":"object"}},"title":"identity_secret"},{"type":"object","properties":{"ok":{"const":true},"conversation":{"type":"object"}},"title":"mute"},{"type":"object","properties":{"ok":{"const":true},"settings":{"type":"object","description":"… plus widget, refused (geweigerde adressen van de laatste 7 dagen) en branding"},"widget_options":{"type":"object"}},"title":"settings"},{"type":"object","properties":{"ok":{"const":true},"settings":{"type":"object"}},"title":"refused_allow"},{"type":"object","properties":{"ok":{"const":true},"settings":{"type":"object"}},"title":"refused_ignore"},{"type":"object","properties":{"ok":{"const":true},"settings":{"type":"object"}},"title":"rotate_token"}]}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/faq":{"get":{"tags":["Advice, questions and AI"],"operationId":"get_faq","summary":"Frequently asked questions","description":"Articles the visitor searches in the chat window. Markdown; `html` is sanitised (no raw HTML, no images, only http(s) and mailto links). With `suggest` at most three published matches for a visitor's question, with `plain` to insert as a reply.\n\n**Who:** Like the chat: team with Website on view (drafts only for owner and member), a client only published articles and only with Website on approve.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"project_id","in":"query","required":false,"schema":{"type":"integer"}},{"name":"q","in":"query","required":false,"schema":{"type":"string"},"description":"Search title, category and body."},{"name":"suggest","in":"query","required":false,"schema":{"type":"string"},"description":"The visitor's question."},{"name":"stats","in":"query","required":false,"schema":{"type":"integer"},"description":"Self-service figures over 7, 30 or 90 days; managers only."},{"name":"id","in":"query","required":false,"schema":{"type":"integer"},"description":"One article with body, html and plain."}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"stats":{"type":"object","description":"With stats: articles (per id views, yes, no, helpful_pct), views, yes, no, misses (searches without results: term, count), conversations_after, deflection_pct."},"articles":{"type":"array","items":{"type":"object"},"description":"id, project_id, title, category, language, published, featured, position, views, updated_at, excerpt (met suggest ook body, html, plain)"},"article":{"type":"object"},"can_manage":{"type":"boolean"},"languages":{"type":"object"},"max_body":{"type":"integer"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Advice, questions and AI"],"operationId":"post_faq","summary":"Manage frequently asked questions","description":"**Who:** Owner or member (Website on manage); `preview` anyone who sees the chat.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"create"},"project_id":{"type":"integer"},"title":{"type":"string","description":"At most 200 characters."},"body":{"type":"string","description":"Markdown, at most 20,000 characters."},"category":{"type":"string"},"language":{"type":"string","enum":["nl","en"]},"published":{"type":"boolean"}},"required":["action","project_id","title","body"],"title":"create"},{"type":"object","properties":{"action":{"const":"update"},"id":{"type":"integer"},"title":{"type":"string"},"body":{"type":"string"},"category":{"type":["string","null"]},"language":{"type":"string","enum":["nl","en"]},"published":{"type":"boolean"},"featured":{"type":"boolean","description":"Featured on the window's start screen."}},"required":["action","id"],"title":"update"},{"type":"object","properties":{"action":{"const":"delete"},"id":{"type":"integer"}},"required":["action","id"],"title":"delete"},{"type":"object","properties":{"action":{"const":"reorder"},"project_id":{"type":"integer"},"ids":{"type":"array","items":{"type":"integer"},"description":"All articles of the project in the new order."}},"required":["action","project_id","ids"],"title":"reorder"},{"type":"object","properties":{"action":{"const":"preview"},"body":{"type":"string"}},"required":["action","body"],"title":"preview","description":"The same HTML the widget shows."}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true},"article":{"type":"object"}},"title":"create"},{"type":"object","properties":{"ok":{"const":true},"article":{"type":"object"}},"title":"update"},{"type":"object","properties":{"ok":{"const":true}},"title":"delete"},{"type":"object","properties":{"ok":{"const":true}},"title":"reorder"},{"type":"object","properties":{"ok":{"const":true},"html":{"type":"string"}},"title":"preview"}]}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/chat-team":{"get":{"tags":["Advice, questions and AI"],"operationId":"get_chat_team","summary":"Chat: reporting and availability","description":"With `view=report` (default): conversations per day, per weekday and hour, first response and resolution time (median and p90, in seconds, only within opening hours), missed (15 minutes without a reply and no ticket), tickets, satisfaction (4 or 5 out of 5 counts as satisfied), busiest pages and per team member. With `view=team`: your own status, the team with status and open conversations, and the routing per project.\n\n**Who:** Like the inbox. Per team member and the routing only for the team; changing the routing: owner or member.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"view","in":"query","required":false,"schema":{"type":"string","enum":["report","team"]}},{"name":"from","in":"query","required":false,"schema":{"type":"string","format":"date"},"description":"Defaults to 27 days before to."},{"name":"to","in":"query","required":false,"schema":{"type":"string","format":"date"},"description":"Defaults to today."},{"name":"project_id","in":"query","required":false,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"report":{"type":"object","description":"from, to, timezone, totals, days, heatmap, pages, comments, agents, truncated"},"projects":{"type":"array","items":{"type":"object"},"description":"id, name"},"me":{"type":"object","description":"With view=team: status, chosen, max_chats, open, can_set."},"team":{"type":"array","items":{"type":"object"},"description":"id, name, status, open, max_chats"},"routing":{"type":"array","items":{"type":"object"},"description":"project_id, name, assign_mode, report_block"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Advice, questions and AI"],"operationId":"post_chat_team","summary":"Chat: status and routing","description":"**Who:** Status: whoever may reply somewhere. Routing: owner or member.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"availability"},"status":{"type":"string","enum":["beschikbaar","afwezig","offline"]},"max_chats":{"type":["integer","null"],"description":"At most this many open conversations at once, 1–50; empty means no limit."}},"required":["action","status"],"title":"availability","description":"Anyone set to available who has not fetched the inbox for ninety seconds counts as away; after fifteen minutes as offline. Offline: your automatically routed conversations go to someone else or become unassigned."},{"type":"object","properties":{"action":{"const":"routing"},"project_id":{"type":"integer"},"assign_mode":{"type":"string","enum":["handmatig","rondgang","minst_bezet"],"description":"Manual: nobody is assigned automatically. Rondgang: round robin among available members. Minst bezet: whoever has the fewest open conversations."},"report_block":{"type":"boolean","description":"Chat in this project's monthly report."}},"required":["action","project_id"],"title":"routing"}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true}},"title":"availability"},{"type":"object","properties":{"ok":{"const":true}},"title":"routing"}]}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/ai":{"get":{"tags":["Advice, questions and AI"],"operationId":"get_ai","summary":"AI tasks, texts, brand voice and AI visibility","description":"**Who:** Team only (owner, member or viewer); clients get 403.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"project_id","in":"query","required":true,"schema":{"type":"integer"},"description":"Project id."}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"kinds":{"type":"object"},"usage":{"type":"object","description":"used, limit, cost_usd"},"worker_seen":{"type":["string","null"],"format":"date-time"},"tasks":{"type":"array","items":{"type":"object"}},"texts":{"type":"array","items":{"type":"object"}},"voice":{"type":["object","null"]},"opportunities":{"type":"array","items":{"type":"object"}},"advice":{"type":"array","items":{"type":"object"}},"reports":{"type":"array","items":{"type":"object"}},"content":{"type":"array","items":{"type":"object"}},"geo":{"type":"object","description":"questions"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Advice, questions and AI"],"operationId":"post_ai","summary":"Request AI tasks and review texts","description":"**Who:** Owner or member; viewers and clients get 403.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"request"},"kind":{"type":"string","enum":["title_meta","advice_plain","report_intro","content_brief","draft_text","week_analysis","data_question","share_of_answer","ad_texts"]},"recommendation_id":{"type":"integer","description":"For title_meta and advice_plain."},"report_id":{"type":"integer","description":"For report_intro."},"content_id":{"type":"integer","description":"For content_brief and draft_text."},"project_id":{"type":"integer","description":"For the others."},"question":{"type":"string","description":"For data_question."},"keyword":{"type":"string","description":"For ad_texts."},"landing":{"type":"string","format":"uri"}},"required":["action","kind"],"title":"request"},{"type":"object","properties":{"action":{"const":"decide"},"text_id":{"type":"integer"},"decision":{"type":"string","enum":["overgenomen","aangepast","afgewezen"]},"final":{"type":"string","description":"Required for aangepast."}},"required":["action","text_id","decision"],"title":"decide"},{"type":"object","properties":{"action":{"const":"voice"},"project_id":{"type":"integer"},"tone":{"type":"string"},"do_words":{"type":"string"},"dont_words":{"type":"string"},"forbidden_claims":{"type":"string"}},"required":["action","project_id"],"title":"voice"},{"type":"object","properties":{"action":{"const":"geo_add"},"project_id":{"type":"integer"},"question":{"type":"string","description":"At most ten per project."}},"required":["action","project_id","question"],"title":"geo_add"},{"type":"object","properties":{"action":{"const":"geo_remove"},"project_id":{"type":"integer"},"id":{"type":"integer"}},"required":["action","project_id","id"],"title":"geo_remove"}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true},"task_id":{"type":"integer"}},"title":"request"},{"type":"object","properties":{"ok":{"const":true}},"title":"decide"},{"type":"object","properties":{"ok":{"const":true}},"title":"voice"},{"type":"object","properties":{"ok":{"const":true}},"title":"geo_add"},{"type":"object","properties":{"ok":{"const":true}},"title":"geo_remove"}]}}}},"401":{"$ref":"#/components/responses/401"},"402":{"$ref":"#/components/responses/402"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/ai/worker":{"get":{"tags":["Advice, questions and AI"],"operationId":"get_ai_worker","summary":"AI worker: claim the next task","description":"Note: this GET claims the task (sets it to running). For instnt's worker only.\n\n**Who:** instnt platform admins only; others get 404 or 403.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"task":{"type":["object","null"],"description":"id, kind, prompt, schema, model"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Advice, questions and AI"],"operationId":"post_ai_worker","summary":"AI worker: submit a result","description":"**Who:** instnt platform admins only; others get 404 or 403.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"integer"},"result":{"type":"object"}},"required":["id","result"]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/uptime":{"get":{"tags":["Uptime and technical"],"operationId":"get_uptime","summary":"Uptime, errors, backups and maintenance","description":"**Who:** Team; clients with access to the Website module.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"project_id","in":"query","required":true,"schema":{"type":"integer"},"description":"Project id."}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"monitors":{"type":"array","items":{"type":"object"},"description":"id, url, expect_status, expect_text, interval_minutes, enabled, state, last_checked_at, last_response_ms, last_error, ssl_expires_at, safe_browsing, uptime_24h, uptime_30d, avg_ms_24h, recent, incidents"},"errors":{"type":"array","items":{"type":"object"},"description":"id, message, source, line, col, page, browser, stack, occurrences, first_seen, last_seen, status, last_14_days"},"error_key":{"type":["string","null"]},"snippet":{"type":["string","null"]},"script_tag":{"type":["string","null"]},"status_page":{"type":"boolean"},"alert_emails":{"type":["string","null"]},"backups":{"type":"array","items":{"type":"object"}},"maintenance":{"type":"array","items":{"type":"object"}},"can_manage":{"type":"boolean"},"suggested_url":{"type":["string","null"],"format":"uri"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Uptime and technical"],"operationId":"post_uptime","summary":"Manage checks, status page, maintenance and backups","description":"**Who:** Owner or member; viewers and clients get 403.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"create"},"project_id":{"type":"integer"},"url":{"type":"string","format":"uri"},"expect_status":{"type":"integer","description":"Defaults to 200."},"expect_text":{"type":"string"},"interval_minutes":{"type":"integer","enum":[1,5,15]}},"required":["action","project_id","url"],"title":"create","description":"Checks once right away."},{"type":"object","properties":{"action":{"const":"update"},"monitor_id":{"type":"integer"},"url":{"type":"string","format":"uri"},"expect_status":{"type":"integer"},"expect_text":{"type":"string"},"interval_minutes":{"type":"integer","enum":[1,5,15]},"enabled":{"type":"boolean"}},"required":["action","monitor_id","url"],"title":"update"},{"type":"object","properties":{"action":{"const":"delete"},"monitor_id":{"type":"integer"}},"required":["action","monitor_id"],"title":"delete"},{"type":"object","properties":{"action":{"const":"check"},"monitor_id":{"type":"integer"}},"required":["action","monitor_id"],"title":"check"},{"type":"object","properties":{"action":{"const":"status_page"},"project_id":{"type":"integer"},"revoke":{"type":"boolean"}},"required":["action","project_id"],"title":"status_page"},{"type":"object","properties":{"action":{"const":"alert_emails"},"project_id":{"type":"integer"},"emails":{"type":"string","description":"Comma-separated."}},"required":["action","project_id"],"title":"alert_emails"},{"type":"object","properties":{"action":{"const":"maintenance_create"},"project_id":{"type":"integer"},"starts_at":{"type":"string","format":"date-time"},"ends_at":{"type":"string","format":"date-time","description":"At most a week after the start."},"note":{"type":"string"}},"required":["action","project_id","starts_at","ends_at"],"title":"maintenance_create"},{"type":"object","properties":{"action":{"const":"maintenance_delete"},"id":{"type":"integer"}},"required":["action","id"],"title":"maintenance_delete"},{"type":"object","properties":{"action":{"const":"backup_create"},"project_id":{"type":"integer"},"name":{"type":"string"},"max_hours":{"type":"integer","description":"1 to 744; defaults to 26."}},"required":["action","project_id","name"],"title":"backup_create"},{"type":"object","properties":{"action":{"const":"backup_delete"},"id":{"type":"integer"}},"required":["action","id"],"title":"backup_delete"},{"type":"object","properties":{"action":{"const":"error_key"},"project_id":{"type":"integer"}},"required":["action","project_id"],"title":"error_key","description":"Creates the site script key if there isn't one."},{"type":"object","properties":{"action":{"const":"error_status"},"error_id":{"type":"integer"},"status":{"type":"string","enum":["open","resolved","ignored"]}},"required":["action","error_id","status"],"title":"error_status"}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true},"monitor_id":{"type":"integer"}},"title":"create"},{"type":"object","properties":{"ok":{"const":true}},"title":"update"},{"type":"object","properties":{"ok":{"const":true}},"title":"delete"},{"type":"object","properties":{"ok":{"const":true}},"title":"check"},{"type":"object","properties":{"ok":{"const":true},"link":{"type":"string","format":"uri"}},"title":"status_page"},{"type":"object","properties":{"ok":{"const":true}},"title":"alert_emails"},{"type":"object","properties":{"ok":{"const":true}},"title":"maintenance_create"},{"type":"object","properties":{"ok":{"const":true}},"title":"maintenance_delete"},{"type":"object","properties":{"ok":{"const":true}},"title":"backup_create"},{"type":"object","properties":{"ok":{"const":true}},"title":"backup_delete"},{"type":"object","properties":{"ok":{"const":true}},"title":"error_key"},{"type":"object","properties":{"ok":{"const":true}},"title":"error_status"}]}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/site-checks":{"get":{"tags":["Uptime and technical"],"operationId":"get_site_checks","summary":"Site checks per host","description":"Headers, exposed files, cookie flags, CMS, domain, DNSSEC and CAA, mail, tags, third parties, robots, AI crawlers and sitemap.\n\n**Who:** Team; clients with access to the Website module.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"project_id","in":"query","required":true,"schema":{"type":"integer"},"description":"Project id."}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"hosts":{"type":"array","items":{"type":"object"},"description":"host, checks"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Uptime and technical"],"operationId":"post_site_checks","summary":"Run site checks now","description":"**Who:** Team; clients with access to the Website module.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"run"},"project_id":{"type":"integer"}},"required":["action","project_id"],"title":"run","description":"Re-runs the checks for the project's hosts right away, including the certificate of monitored addresses, and recomputes the security score. At most twice per five minutes per project; after that 429."}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true},"hosts":{"type":"array","items":{"type":"object"},"description":"host, checks"}},"title":"run"}]}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/security-score":{"get":{"tags":["Uptime and technical"],"operationId":"get_security_score","summary":"Security score","description":"Score 0–100 from six weighted parts (encryption, headers, exposure, software, mail and DNS, reputation), computed from the latest site check, certificates, vulnerabilities and Safe Browsing. Parts that were not measured do not count (their `score` is `null`); with fewer than two measured parts `score` itself is `null`. `trend` is the daily score of the last 30 days. `domains` tells per domain whether it is verified and how (`dns` or `plugin`); the TXT record is at /api/domains.\n\n**Who:** Team; clients with access to the Website module.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"project_id","in":"query","required":true,"schema":{"type":"integer"},"description":"Project id."}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"score":{"type":["integer","null"]},"previous":{"type":["integer","null"]},"trend":{"type":"array","items":{"type":"object"},"description":"day, score"},"categories":{"type":"array","items":{"type":"object"},"description":"key, label, weight, description, score (?int), points (status, label, detail)"},"method":{"type":"string"},"domains":{"type":"array","items":{"type":"object"},"description":"domain, verified, via"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Uptime and technical"],"operationId":"post_security_score","summary":"Recompute the security score","description":"**Who:** Owner or member (manages Website); others get 404.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"recompute"},"project_id":{"type":"integer"}},"required":["action","project_id"],"title":"recompute","description":"Recounts from the latest measurements and stores today's score; fetches nothing. The checks themselves run every morning at 05:50."}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true},"score":{"type":["integer","null"]}},"title":"recompute"}]}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/tech-stack":{"get":{"tags":["Uptime and technical"],"operationId":"get_tech_stack","summary":"What runs on the site","description":"Detected technology per category from the homepage and headers (CMS, shop, framework, analytics, ads, consent, payments, reviews, chat, hosting, fonts), what was added or removed in the last 90 days, detected services that are not connected yet, setup gaps and free suggestions. Rule-based; `summary` is an optional AI paragraph.\n\n**Who:** Team; clients with access to the Website module.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"project_id","in":"query","required":true,"schema":{"type":"integer"},"description":"Project id."}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"hosts":{"type":"array","items":{"type":"string"}},"checked_at":{"type":["string","null"],"format":"date-time"},"detected":{"type":"array","items":{"type":"object"},"description":"category, label, items (tech, label, version, evidence, host, since, last_seen, connector, connected)"},"changes":{"type":"array","items":{"type":"object"},"description":"type (added|removed), label, host, at"},"connect":{"type":"array","items":{"type":"object"},"description":"tech, label, connector, url"},"gaps":{"type":"array","items":{"type":"object"},"description":"key, tone, title, detail, url, action"},"suggestions":{"type":"array","items":{"type":"object"},"description":"key, title, detail, url, action"},"summary":{"type":["object","null"],"description":"status, text, at"},"can_manage":{"type":"boolean"},"can_ai":{"type":"boolean"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Uptime and technical"],"operationId":"post_tech_stack","summary":"Scan again or request a summary","description":"**Who:** Owner or member; viewers and clients get 403.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"scan"},"project_id":{"type":"integer"}},"required":["action","project_id"],"title":"scan","description":"Looks at the homepage(s) again now. At most once a minute (429)."},{"type":"object","properties":{"action":{"const":"summary"},"project_id":{"type":"integer"}},"required":["action","project_id"],"title":"summary","description":"Own team only. Claude writes one paragraph about the detection; it then appears as `summary` in the GET."}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true}},"title":"scan"},{"type":"object","properties":{"ok":{"const":true},"task_id":{"type":"integer"}},"title":"summary"}]}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/components":{"get":{"tags":["Uptime and technical"],"operationId":"get_components","summary":"Versions and open vulnerabilities","description":"**Who:** Team; clients with access to the Website module.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"project_id","in":"query","required":true,"schema":{"type":"integer"},"description":"Project id."}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"components":{"type":"array","items":{"type":"object"},"description":"ecosystem, name, version, type, dev, active, reported_at"},"reported":{"type":"object"},"vulns":{"type":"array","items":{"type":"object"},"description":"ecosystem, name, version, vuln_id, aliases, severity, summary, fixed_in, url, first_seen_at"},"resolved_30d":{"type":"integer"},"wordfence":{"type":"boolean"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Server to server"],"operationId":"post_components","summary":"Lock files for the vulnerability check","description":"With a deploy key that belongs to a project. Each report replaces the previous set for that ecosystem. At most 5 MB in total.\n\n**Access:** Deploy key: `Authorization: Bearer <key>`","security":[{"deployToken":[]}],"x-studio-auth":"deploy","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"files":{"type":"object","description":"File name to contents: composer.lock, package-lock.json, Package.resolved, Cargo.lock."}},"required":["files"]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"counts":{"type":"object"}}}}}},"401":{"$ref":"#/components/responses/401"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/cookiescan":{"get":{"tags":["Uptime and technical"],"operationId":"get_cookiescan","summary":"Latest cookie scan with changes","description":"**Who:** Team; clients with access to the Website module.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"project_id","in":"query","required":true,"schema":{"type":"integer"},"description":"Project id."}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"latest":{"type":["object","null"]},"last_error":{"type":["object","null"]},"findings":{"type":"array","items":{"type":"object"}},"gone":{"type":"array","items":{"type":"string"}},"has_previous":{"type":"boolean"},"history":{"type":"array","items":{"type":"object"}},"can_scan":{"type":"boolean"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Uptime and technical"],"operationId":"post_cookiescan","summary":"Scan now","description":"**Who:** Owner or member (manages Website); others get 404.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"scan"},"project_id":{"type":"integer"}},"required":["action","project_id"],"title":"scan","description":"At most once per five minutes per project."}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true},"task_id":{"type":"string","description":"Follow the task with GET /api/task?id=…"}},"title":"scan"}]}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/reports":{"get":{"tags":["Reports"],"operationId":"get_reports","summary":"Reports of a project","description":"Without `period`: the list. With `period`: one report with data. With `range`: a quarter (`2026-Q3`) or year (`2026`) built from the monthly reports.\n\n**Who:** Team; clients with access to the Overview module.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"project_id","in":"query","required":true,"schema":{"type":"integer"},"description":"Project id."},{"name":"period","in":"query","required":false,"schema":{"type":"string","pattern":"^\\d{4}-(0[1-9]|1[0-2])$"}},{"name":"range","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"reports":{"type":"array","items":{"type":"object"},"description":"id, period, label, status, intro, generated_at, published_at"},"report":{"type":"object"},"period_report":{"type":"object"},"suggested_period":{"type":"string","pattern":"^\\d{4}-(0[1-9]|1[0-2])$"},"can_manage":{"type":"boolean"},"project":{"type":"string"},"branding":{"type":"object"},"shares":{"type":"array","items":{"type":"object"}}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Reports"],"operationId":"post_reports","summary":"Generate, publish or share a report","description":"**Who:** Owner or member; viewers and clients get 403.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"generate"},"project_id":{"type":"integer"},"period":{"type":"string","pattern":"^\\d{4}-(0[1-9]|1[0-2])$","description":"A past month; defaults to the previous one."}},"required":["action","project_id"],"title":"generate","description":"Creates the report or refreshes its figures; the introduction stays."},{"type":"object","properties":{"action":{"const":"intro"},"id":{"type":"integer"},"intro":{"type":"string"}},"required":["action","id"],"title":"intro"},{"type":"object","properties":{"action":{"const":"publish"},"id":{"type":"integer"}},"required":["action","id"],"title":"publish","description":"Triggers the automation that emails the client."},{"type":"object","properties":{"action":{"const":"unpublish"},"id":{"type":"integer"}},"required":["action","id"],"title":"unpublish"},{"type":"object","properties":{"action":{"const":"delete"},"id":{"type":"integer"}},"required":["action","id"],"title":"delete"},{"type":"object","properties":{"action":{"const":"share"},"id":{"type":"integer"},"days":{"type":"integer","description":"1 to 90; defaults to 30."}},"required":["action","id"],"title":"share","description":"Published reports only."},{"type":"object","properties":{"action":{"const":"unshare"},"id":{"type":"integer"},"share_id":{"type":"integer"}},"required":["action","id","share_id"],"title":"unshare"}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true},"task_id":{"type":"string","description":"Follow the task with GET /api/task?id=…"},"period":{"type":"string","pattern":"^\\d{4}-(0[1-9]|1[0-2])$"}},"title":"generate"},{"type":"object","properties":{"ok":{"const":true}},"title":"intro"},{"type":"object","properties":{"ok":{"const":true},"notified":{"type":"integer"}},"title":"publish"},{"type":"object","properties":{"ok":{"const":true}},"title":"unpublish"},{"type":"object","properties":{"ok":{"const":true}},"title":"delete"},{"type":"object","properties":{"ok":{"const":true},"link":{"type":"string","format":"uri"},"expires_at":{"type":"string","format":"date"}},"title":"share"},{"type":"object","properties":{"ok":{"const":true}},"title":"unshare"}]}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/reports/pdf":{"get":{"tags":["Reports"],"operationId":"get_reports_pdf","summary":"Report as PDF","description":"**Who:** Team; clients with access to the Overview module.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"id","in":"query","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Success.","content":{"application/pdf":{"schema":{"type":"string","format":"binary"}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/ads":{"get":{"tags":["Google Ads"],"operationId":"get_ads","summary":"Google Ads for a project","description":"**Who:** Team; clients with access to the Ads module.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"project_id","in":"query","required":true,"schema":{"type":"integer"},"description":"Project id."}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"summary":{"type":["object","null"]},"pacing":{"type":"array","items":{"type":"object"}},"ngrams":{"type":["object","null"]},"quality":{"type":["object","null"]},"budget":{"type":["object","null"]},"ad_tests":{"type":"array","items":{"type":"object"}},"pmax":{"type":"array","items":{"type":"object"}},"can_manage":{"type":"boolean"},"autopilot":{"type":["object","null"],"description":"Only for those who manage: mode, max_change_pct, max_daily_budget, max_changes_per_day, max_week_change_pct, allowed_kinds, excluded_campaigns, quiet_from, quiet_until, enabled_at, enabled_by, stopped_at, killed_at, auto_apply, campaigns, me (two_factor, owner, reauth), proposals (with effect, reversible, can_revert, auto_blocked) and changes (the log: action, result, before, after, request_id, user)."}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Google Ads"],"operationId":"post_ads","summary":"Targets, ideas and the Ads pilot","description":"**Who:** Owner or member; viewers and clients get 403.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"targets"},"project_id":{"type":"integer"},"target_cpa":{"type":"number"},"order_value":{"type":"number"},"margin_pct":{"type":"number"}},"required":["action","project_id"],"title":"targets"},{"type":"object","properties":{"action":{"const":"ideas"},"project_id":{"type":"integer"},"seeds":{"type":"array","items":{"type":"string"},"description":"At most ten."}},"required":["action","project_id","seeds"],"title":"ideas","description":"Via Keyword Planner; cached for a week."},{"type":"object","properties":{"action":{"const":"mode"},"project_id":{"type":"integer"},"mode":{"type":"string","enum":["uit","lezen","voorstellen","automatisch"]},"max_change_pct":{"type":"integer","description":"1 to 100; defaults to 20."},"max_daily_budget":{"type":"number"},"max_changes_per_day":{"type":"integer","description":"1 to 50; defaults to 5."},"max_week_change_pct":{"type":"integer","description":"How far a budget may move in seven days; defaults to 30."},"allowed_kinds":{"type":"array","items":{"type":"string"},"description":"budget, negative and/or pause_keyword; defaults to budget and negative."},"excluded_campaigns":{"type":"array","items":{"type":"integer"}},"quiet_from":{"type":["integer","null"],"description":"Hour 0–23; together with quiet_until."},"quiet_until":{"type":["integer","null"]},"password":{"type":"string"},"code":{"type":"string"}},"required":["action","project_id","mode"],"title":"mode","description":"`automatisch` and wider limits only by the owner, with two-factor on and the `code` from the authenticator (valid ten minutes; a password is not enough since 11-10-2026). Without a valid code 422 `reauth`. Not with an API key (403); `stop` and `kill` are allowed."},{"type":"object","properties":{"action":{"const":"propose"},"project_id":{"type":"integer"}},"required":["action","project_id"],"title":"propose"},{"type":"object","properties":{"action":{"const":"apply"},"project_id":{"type":"integer"},"proposal_id":{"type":"integer"},"confirm_irreversible":{"type":"boolean","description":"Required for what cannot be undone (Google's recommendations)."},"password":{"type":"string"},"code":{"type":"string"}},"required":["action","project_id","proposal_id"],"title":"apply","description":"Requires two-factor. Sent to Google with validateOnly first, then for real; every outcome goes into the log."},{"type":"object","properties":{"action":{"const":"reject"},"project_id":{"type":"integer"},"proposal_id":{"type":"integer"},"reason":{"type":"string"}},"required":["action","project_id","proposal_id"],"title":"reject"},{"type":"object","properties":{"action":{"const":"revert"},"project_id":{"type":"integer"},"proposal_id":{"type":"integer"},"password":{"type":"string"},"code":{"type":"string"}},"required":["action","project_id","proposal_id"],"title":"revert","description":"Requires two-factor; allowed even with the kill switch on."},{"type":"object","properties":{"action":{"const":"stop"},"project_id":{"type":"integer"}},"required":["action","project_id"],"title":"stop","description":"Emergency stop for this project: back to read."},{"type":"object","properties":{"action":{"const":"kill"},"project_id":{"type":"integer"}},"required":["action","project_id"],"title":"kill","description":"Kill switch for every project in the workspace; no two-factor, so stopping is always quick."},{"type":"object","properties":{"action":{"const":"release"},"project_id":{"type":"integer"},"password":{"type":"string"},"code":{"type":"string"}},"required":["action","project_id"],"title":"release","description":"Turn the workspace kill switch off: owner only, with two-factor."}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true}},"title":"targets"},{"type":"object","properties":{"ok":{"const":true},"ideas":{"type":"array","items":{"type":"object"}},"seeds":{"type":"array","items":{"type":"string"}}},"title":"ideas"},{"type":"object","properties":{"ok":{"const":true}},"title":"mode"},{"type":"object","properties":{"ok":{"const":true}},"title":"propose"},{"type":"object","properties":{"ok":{"const":true},"proposal":{"type":"object"}},"title":"apply"},{"type":"object","properties":{"ok":{"const":true}},"title":"reject"},{"type":"object","properties":{"ok":{"const":true}},"title":"revert"},{"type":"object","properties":{"ok":{"const":true}},"title":"stop"},{"type":"object","properties":{"ok":{"const":true}},"title":"kill"},{"type":"object","properties":{"ok":{"const":true}},"title":"release"}]}}}},"401":{"$ref":"#/components/responses/401"},"402":{"$ref":"#/components/responses/402"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/automations":{"get":{"tags":["Automations and alerts"],"operationId":"get_automations","summary":"Automations, catalogue, log and approvals","description":"Without parameters: the list plus the trigger and action catalogue. `runs=1`: the log (25 per page). `run=ID`: one run. `approvals=1`: what awaits approval.\n\n**Who:** Team only (owner, member or viewer); clients get 403.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"runs","in":"query","required":false,"schema":{"type":"boolean"}},{"name":"run","in":"query","required":false,"schema":{"type":"integer"}},{"name":"approvals","in":"query","required":false,"schema":{"type":"boolean"}},{"name":"automation_id","in":"query","required":false,"schema":{"type":"integer"}},{"name":"status","in":"query","required":false,"schema":{"type":"string"}},{"name":"project_id","in":"query","required":false,"schema":{"type":"integer"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"automations":{"type":"array","items":{"type":"object"},"description":"id, name, enabled, is_default, trigger_type, trigger_params, project_id, filters, actions, rate_limit, requires_approval, fired, fired_30d, last_run, hook_url, schedule, can_run"},"catalog":{"type":"object"},"action_catalog":{"type":"object"},"templates":{"type":"array","items":{"type":"object"}},"triggers":{"type":"array","items":{"type":"object"}},"runs":{"type":"array","items":{"type":"object"}},"total":{"type":"integer"},"pages":{"type":"integer"},"run":{"type":"object"},"approvals":{"type":"array","items":{"type":"object"}},"limit":{"type":["integer","null"]},"used":{"type":"integer"},"timezone":{"type":"string"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Automations and alerts"],"operationId":"post_automations","summary":"Save, test and run automations","description":"**Who:** Owner or member; viewers and clients get 403.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"save"},"id":{"type":"integer","description":"Without id: new."},"name":{"type":"string"},"trigger_type":{"type":"string","description":"From `catalog`."},"trigger_params":{"type":"object"},"project_id":{"type":["integer","null"]},"filters":{"type":"array","items":{"type":"object"}},"actions":{"type":"array","items":{"type":"object"},"description":"Each {type, params}; if-then-else is type branch with then and else."},"rate_limit":{"type":"object"},"requires_approval":{"type":"boolean","description":"Only the owner can turn this off."},"enabled":{"type":"boolean"}},"required":["action","name","trigger_type","actions"],"title":"save","description":"Errors come back per field in `fields`."},{"type":"object","properties":{"action":{"const":"test"},"id":{"type":"integer"},"run_id":{"type":"integer","description":"Data from an earlier run instead of the example."}},"required":["action"],"title":"test","description":"Dry run with the same fields as save, or just an id; validates, but writes and sends nothing."},{"type":"object","properties":{"action":{"const":"timezone"},"timezone":{"type":"string","description":"An IANA name, such as Europe/Amsterdam."}},"required":["action","timezone"],"title":"timezone"},{"type":"object","properties":{"action":{"const":"toggle"},"id":{"type":"integer"}},"required":["action","id"],"title":"toggle"},{"type":"object","properties":{"action":{"const":"delete"},"id":{"type":"integer"}},"required":["action","id"],"title":"delete","description":"A default automation is switched off instead of deleted."},{"type":"object","properties":{"action":{"const":"run"},"id":{"type":"integer"}},"required":["action","id"],"title":"run","description":"Only for a manual trigger or a schedule."},{"type":"object","properties":{"action":{"const":"rotate_hook"},"id":{"type":"integer"}},"required":["action","id"],"title":"rotate_hook","description":"New address for an incoming webhook; the old one stops working at once."},{"type":"object","properties":{"action":{"const":"reset"},"id":{"type":"integer"}},"required":["action","id"],"title":"reset"},{"type":"object","properties":{"action":{"const":"retry"},"run_id":{"type":"integer"},"action_index":{"type":"integer"}},"required":["action","run_id","action_index"],"title":"retry"},{"type":"object","properties":{"action":{"const":"approve"},"approval_id":{"type":"integer"}},"required":["action","approval_id"],"title":"approve"},{"type":"object","properties":{"action":{"const":"reject"},"approval_id":{"type":"integer"}},"required":["action","approval_id"],"title":"reject"}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true},"id":{"type":"integer"}},"title":"save"},{"type":"object","properties":{"ok":{"const":true},"source":{"type":"string"},"data":{"type":"object"}},"additionalProperties":true,"title":"test"},{"type":"object","properties":{"ok":{"const":true},"timezone":{"type":"string"}},"title":"timezone"},{"type":"object","properties":{"ok":{"const":true}},"title":"toggle"},{"type":"object","properties":{"ok":{"const":true}},"title":"delete"},{"type":"object","properties":{"ok":{"const":true},"run_id":{"type":["integer","null"]}},"title":"run"},{"type":"object","properties":{"ok":{"const":true}},"title":"rotate_hook"},{"type":"object","properties":{"ok":{"const":true}},"title":"reset"},{"type":"object","properties":{"ok":{"const":true}},"title":"retry"},{"type":"object","properties":{"ok":{"const":true}},"title":"approve"},{"type":"object","properties":{"ok":{"const":true}},"title":"reject"}]}}}},"401":{"$ref":"#/components/responses/401"},"402":{"$ref":"#/components/responses/402"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/alerts":{"get":{"tags":["Automations and alerts"],"operationId":"get_alerts","summary":"Alert rules and what was sent","description":"**Who:** Team only (owner, member or viewer); clients get 403.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"emails":{"type":"array","items":{"type":"string"}},"rules":{"type":"array","items":{"type":"object"}},"automatic":{"type":"array","items":{"type":"object"}},"log":{"type":"array","items":{"type":"object"}},"approvals_open":{"type":"integer"},"actions":{"type":"object"},"recipients":{"type":"object"},"channels":{"type":"array","items":{"type":"object"}},"webhooks":{"type":"array","items":{"type":"object"}},"team":{"type":"array","items":{"type":"object"}},"can_edit":{"type":"boolean"},"is_owner":{"type":"boolean"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Automations and alerts"],"operationId":"post_alerts","summary":"Create or change an alert rule","description":"**Who:** Owner or member; viewers and clients get 403.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"create"},"kind":{"type":"string","enum":["threshold_live","milestone","event"]},"project_id":{"type":["integer","null"],"description":"Required for event."},"metric":{"type":"string"},"threshold":{"type":"integer","description":"At least 1."},"name":{"type":"string"},"actions":{"type":"array","items":{"type":"object"}}},"required":["action","kind","threshold"],"title":"create"},{"type":"object","properties":{"action":{"const":"update"},"rule_id":{"type":"integer"},"threshold":{"type":"integer"},"metric":{"type":"string"},"project_id":{"type":["integer","null"]},"name":{"type":"string"},"actions":{"type":"array","items":{"type":"object"}},"requires_approval":{"type":"boolean"},"enabled":{"type":"boolean"}},"required":["action","rule_id"],"title":"update"},{"type":"object","properties":{"action":{"const":"delete"},"rule_id":{"type":"integer"}},"required":["action","rule_id"],"title":"delete"},{"type":"object","properties":{"action":{"const":"toggle"},"rule_id":{"type":"integer"},"enabled":{"type":"boolean"}},"required":["action","rule_id","enabled"],"title":"toggle"}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true},"rule_id":{"type":"integer"}},"title":"create"},{"type":"object","properties":{"ok":{"const":true}},"title":"update"},{"type":"object","properties":{"ok":{"const":true}},"title":"delete"},{"type":"object","properties":{"ok":{"const":true}},"title":"toggle"}]}}}},"401":{"$ref":"#/components/responses/401"},"402":{"$ref":"#/components/responses/402"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/webhooks":{"get":{"tags":["Automations and alerts"],"operationId":"get_webhooks","summary":"Outgoing webhooks","description":"**Who:** Owner or member; viewers and clients get 403.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"webhooks":{"type":"array","items":{"type":"object"},"description":"id, url, events, last_status, last_error, last_at, created_at"},"events":{"type":"object"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Automations and alerts"],"operationId":"post_webhooks","summary":"Add, test or remove a webhook","description":"**Who:** Owner or member; viewers and clients get 403.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"create"},"url":{"type":"string","format":"uri","description":"A public https address."},"events":{"type":"array","items":{"type":"string"},"description":"advice.created, incident.opened, incident.resolved, report.published, anomaly.detected, question.asked, vulnerability.found."}},"required":["action","url","events"],"title":"create","description":"At most ten per workspace."},{"type":"object","properties":{"action":{"const":"delete"},"id":{"type":"integer"}},"required":["action","id"],"title":"delete"},{"type":"object","properties":{"action":{"const":"test"},"id":{"type":"integer"}},"required":["action","id"],"title":"test","description":"Sends `{\"event\": \"test\"}`."}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true},"id":{"type":"integer"},"secret":{"type":"string","description":"Shown once."}},"title":"create"},{"type":"object","properties":{"ok":{"const":true}},"title":"delete"},{"type":"object","properties":{"ok":{"const":true}},"title":"test"}]}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/chat":{"get":{"tags":["Automations and alerts"],"operationId":"get_chat","summary":"Slack and Teams channels","description":"**Who:** Owner or member; viewers and clients get 403.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"channels":{"type":"array","items":{"type":"object"},"description":"id, kind, name, events, last_status, last_at"},"events":{"type":"object"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Automations and alerts"],"operationId":"post_chat","summary":"Add, test or remove a channel","description":"**Who:** Owner or member; viewers and clients get 403.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"create"},"kind":{"type":"string","enum":["slack","teams"]},"name":{"type":"string"},"url":{"type":"string","format":"uri","description":"Webhook address from Slack or Teams."},"events":{"type":"array","items":{"type":"string"}}},"required":["action","kind","url","events"],"title":"create"},{"type":"object","properties":{"action":{"const":"delete"},"id":{"type":"integer"}},"required":["action","id"],"title":"delete"},{"type":"object","properties":{"action":{"const":"test"},"id":{"type":"integer"}},"required":["action","id"],"title":"test"}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true}},"title":"create"},{"type":"object","properties":{"ok":{"const":true}},"title":"delete"},{"type":"object","properties":{"ok":{"const":true},"status":{"type":["string","null"]}},"title":"test"}]}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/slack":{"get":{"tags":["Automations and alerts"],"operationId":"get_slack","summary":"Slack bot: settings without secrets","description":"**Who:** Owner or member; viewers and clients get 403.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"app":{"type":["object","null"],"description":"channel, events, last_status, last_at"},"events":{"type":"array","items":{"type":"string"}},"interactivity_url":{"type":"string","format":"uri"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Automations and alerts"],"operationId":"post_slack","summary":"Configure the Slack bot","description":"**Who:** Owner or member; viewers and clients get 403.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"save"},"bot_token":{"type":"string","description":"xoxb-…; empty keeps the current one."},"signing_secret":{"type":"string"},"channel":{"type":"string","description":"Channel ID (C…)."},"events":{"type":"array","items":{"type":"string"}}},"required":["action","channel"],"title":"save"},{"type":"object","properties":{"action":{"const":"remove"}},"required":["action"],"title":"remove"},{"type":"object","properties":{"action":{"const":"test"}},"required":["action"],"title":"test"}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true}},"title":"save"},{"type":"object","properties":{"ok":{"const":true}},"title":"remove"},{"type":"object","properties":{"ok":{"const":true},"status":{"type":["string","null"]}},"title":"test"}]}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/contacts":{"get":{"tags":["Studio Mail"],"operationId":"get_contacts","summary":"Contacts, lists, forms, templates and domains","description":"Without parameters: the overview. `list=contacten`: contacts with filters (50 per page). `contact_id=…`: one contact with timeline, lists and funnels.\n\n**Who:** Team only (owner, member or viewer); clients get 403.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"list","in":"query","required":false,"schema":{"type":"string","enum":["contacten"]}},{"name":"contact_id","in":"query","required":false,"schema":{"type":"integer"}},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["unconfirmed","subscribed","unsubscribed","bounced","complained"]}},{"name":"project_id","in":"query","required":false,"schema":{"type":"integer"}},{"name":"list_id","in":"query","required":false,"schema":{"type":"integer"}},{"name":"tag","in":"query","required":false,"schema":{"type":"string"}},{"name":"min_score","in":"query","required":false,"schema":{"type":"number"}},{"name":"segment_id","in":"query","required":false,"schema":{"type":"integer"}},{"name":"q","in":"query","required":false,"schema":{"type":"string"}},{"name":"sort","in":"query","required":false,"schema":{"type":"string","enum":["score"]}},{"name":"page","in":"query","required":false,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"providers":{"type":"array","items":{"type":"object"}},"counts":{"type":"object"},"tags":{"type":"array","items":{"type":"string"}},"open_tracking":{"type":"boolean"},"segments":{"type":"array","items":{"type":"object"}},"lists":{"type":"array","items":{"type":"object"}},"forms":{"type":"array","items":{"type":"object"}},"templates":{"type":"array","items":{"type":"object"}},"domains":{"type":"array","items":{"type":"object"}},"accounts":{"type":"array","items":{"type":"object"}},"regions":{"type":"object"},"contacts":{"type":"array","items":{"type":"object"},"description":"id, email, name, status, source, project_id, tags, consent_at, created_at, score"},"total":{"type":"integer"},"page":{"type":"integer"},"contact":{"type":"object"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Studio Mail"],"operationId":"post_contacts","summary":"Manage contacts, forms, templates, domains and accounts","description":"**Who:** Owner or member; viewers and clients get 403.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"contact.update"},"id":{"type":"integer"},"name":{"type":"string"},"language":{"type":"string","enum":["nl","en"],"description":"Language of the confirmation and unsubscribe pages and emails."},"tags":{"type":"array","items":{"type":"string"}},"project_id":{"type":"integer"},"list_ids":{"type":"array","items":{"type":"integer"}}},"required":["action","id"],"title":"contact.update"},{"type":"object","properties":{"action":{"const":"contact.unsubscribe"},"id":{"type":"integer"}},"required":["action","id"],"title":"contact.unsubscribe"},{"type":"object","properties":{"action":{"const":"contact.delete"},"id":{"type":"integer"}},"required":["action","id"],"title":"contact.delete","description":"Also erases the contact's tracking data."},{"type":"object","properties":{"action":{"const":"list.save"},"id":{"type":"integer"},"name":{"type":"string"}},"required":["action","name"],"title":"list.save"},{"type":"object","properties":{"action":{"const":"list.delete"},"id":{"type":"integer"}},"required":["action","id"],"title":"list.delete"},{"type":"object","properties":{"action":{"const":"form.save"},"id":{"type":"integer"},"name":{"type":"string"},"language":{"type":"string","enum":["nl","en"],"description":"Language of the form and embed code; defaults to nl."},"consent_text":{"type":"string"},"list_id":{"type":"integer"},"domain_id":{"type":"integer"},"project_id":{"type":"integer"},"redirect_url":{"type":"string","format":"uri","description":"Thank-you page, https."}},"required":["action","name","consent_text"],"title":"form.save"},{"type":"object","properties":{"action":{"const":"form.delete"},"id":{"type":"integer"}},"required":["action","id"],"title":"form.delete"},{"type":"object","properties":{"action":{"const":"import"},"list_id":{"type":"integer"},"confirmed":{"type":"boolean","description":"Confirmation that everyone consented."},"csv":{"type":"string","description":"Email address and name per line, optionally a third column with the language (nl or en); at most 2 MB."},"declaration":{"type":"string"}},"required":["action","confirmed","csv","declaration"],"title":"import","description":"Rejects the import when more than 20% of rows are invalid; role addresses (info@, sales@ …) are skipped."},{"type":"object","properties":{"action":{"const":"template.save"},"id":{"type":"integer"},"name":{"type":"string"},"subject":{"type":"string"},"mjml":{"type":"string"},"html":{"type":"string"},"text":{"type":"string"}},"required":["action","name","subject","html"],"title":"template.save"},{"type":"object","properties":{"action":{"const":"template.delete"},"id":{"type":"integer"}},"required":["action","id"],"title":"template.delete"},{"type":"object","properties":{"action":{"const":"template.test"},"id":{"type":"integer"},"confirmed":{"type":"boolean"},"domain_id":{"type":"integer"}},"required":["action","id","confirmed"],"title":"template.test","description":"Test email to yourself."},{"type":"object","properties":{"action":{"const":"domain.add"},"domain":{"type":"string"},"from_local":{"type":"string","description":"Defaults to nieuws."},"from_name":{"type":"string"},"provider":{"type":"string","enum":["ses","resend"]},"account_id":{"type":"integer","description":"Own sending account; the provider follows from it."},"project_id":{"type":"integer"}},"required":["action","domain"],"title":"domain.add"},{"type":"object","properties":{"action":{"const":"domain.check"},"id":{"type":"integer"}},"required":["action","id"],"title":"domain.check"},{"type":"object","properties":{"action":{"const":"domain.resume"},"id":{"type":"integer"}},"required":["action","id"],"title":"domain.resume","description":"Lifts an automatic pause."},{"type":"object","properties":{"action":{"const":"domain.delete"},"id":{"type":"integer"}},"required":["action","id"],"title":"domain.delete"},{"type":"object","properties":{"action":{"const":"account.save"},"id":{"type":"integer"},"provider":{"type":"string","enum":["ses","resend"]},"name":{"type":"string"},"project_id":{"type":"integer"},"credentials":{"type":"object","description":"Resend: key, webhook_secret, region. SES: key, secret, region, configuration_set, sns_topic_arn. Empty keeps the current value."}},"required":["action","name"],"title":"account.save","description":"Tests the key right away. Keys never come back; only the last four characters."},{"type":"object","properties":{"action":{"const":"account.test"},"id":{"type":"integer"}},"required":["action","id"],"title":"account.test"},{"type":"object","properties":{"action":{"const":"account.delete"},"id":{"type":"integer"}},"required":["action","id"],"title":"account.delete","description":"Only once no domains use it."},{"type":"object","properties":{"action":{"const":"settings"},"open_tracking":{"type":"boolean"}},"required":["action"],"title":"settings"}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true}},"title":"contact.update"},{"type":"object","properties":{"ok":{"const":true}},"title":"contact.unsubscribe"},{"type":"object","properties":{"ok":{"const":true}},"title":"contact.delete"},{"type":"object","properties":{"ok":{"const":true},"id":{"type":"integer"}},"title":"list.save"},{"type":"object","properties":{"ok":{"const":true}},"title":"list.delete"},{"type":"object","properties":{"ok":{"const":true},"id":{"type":"integer"}},"title":"form.save"},{"type":"object","properties":{"ok":{"const":true}},"title":"form.delete"},{"type":"object","properties":{"ok":{"const":true},"imported":{"type":"boolean"},"added":{"type":"integer"},"updated":{"type":"integer"},"invalid":{"type":"integer"},"role":{"type":"integer"},"duplicates":{"type":"integer"},"suppressed":{"type":"integer"}},"title":"import"},{"type":"object","properties":{"ok":{"const":true},"id":{"type":"integer"}},"title":"template.save"},{"type":"object","properties":{"ok":{"const":true}},"title":"template.delete"},{"type":"object","properties":{"ok":{"const":true},"sent_to":{"type":"string","format":"email"}},"title":"template.test"},{"type":"object","properties":{"ok":{"const":true},"domain":{"type":"object"}},"title":"domain.add"},{"type":"object","properties":{"ok":{"const":true},"domain":{"type":"object"}},"title":"domain.check"},{"type":"object","properties":{"ok":{"const":true}},"title":"domain.resume"},{"type":"object","properties":{"ok":{"const":true}},"title":"domain.delete"},{"type":"object","properties":{"ok":{"const":true},"account":{"type":"object"}},"title":"account.save"},{"type":"object","properties":{"ok":{"const":true},"account":{"type":"object"}},"title":"account.test"},{"type":"object","properties":{"ok":{"const":true}},"title":"account.delete"},{"type":"object","properties":{"ok":{"const":true}},"title":"settings"}]}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/mail":{"get":{"tags":["Studio Mail"],"operationId":"get_mail","summary":"Funnels, campaigns and segments","description":"`funnel=ID`: the funnel and its emails; with `contacts=1` (and `step`, `status`, `page`) the contacts in it. `campaign=ID`: one campaign with results.\n\n**Who:** Team only (owner, member or viewer); clients get 403.\n\n**Access:** Session or API key (read or write)","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","parameters":[{"name":"funnel","in":"query","required":false,"schema":{"type":"integer"}},{"name":"contacts","in":"query","required":false,"schema":{"type":"boolean"}},{"name":"step","in":"query","required":false,"schema":{"type":"integer"}},{"name":"status","in":"query","required":false,"schema":{"type":"string"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer"}},{"name":"campaign","in":"query","required":false,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"providers":{"type":"array","items":{"type":"object"}},"attribution":{"type":"boolean"},"funnels":{"type":"array","items":{"type":"object"}},"campaigns":{"type":"array","items":{"type":"object"}},"segments":{"type":"array","items":{"type":"object"}},"segment_fields":{"type":"array","items":{"type":"object"}},"segment_ops":{"type":"object"},"goals":{"type":"object"},"funnel":{"type":"object"},"campaign":{"type":"object"}}}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Studio Mail"],"operationId":"post_mail","summary":"Segments and campaigns","description":"**Who:** Owner or member; viewers and clients get 403.\n\n**Access:** Session or API key with write access","security":[{"session":[]},{"apiKey":[]}],"x-studio-auth":"key","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"action":{"const":"segment.preview"},"definition":{"type":"object","description":"{match: all|any, rules: [{field, op, value}]}, at most 20 rules."}},"required":["action","definition"],"title":"segment.preview"},{"type":"object","properties":{"action":{"const":"segment.save"},"id":{"type":"integer"},"name":{"type":"string"},"definition":{"type":"object"}},"required":["action","name","definition"],"title":"segment.save"},{"type":"object","properties":{"action":{"const":"segment.delete"},"id":{"type":"integer"}},"required":["action","id"],"title":"segment.delete"},{"type":"object","properties":{"action":{"const":"campaign.save"},"id":{"type":"integer"},"name":{"type":"string"},"template_id":{"type":"integer"},"domain_id":{"type":"integer"},"list_id":{"type":"integer"},"segment_id":{"type":"integer"},"subject_b":{"type":"string","description":"Second subject for an A/B test."},"ab_percent":{"type":"integer","description":"5 to 50."},"ab_hours":{"type":"integer","description":"1 to 72."}},"required":["action","name"],"title":"campaign.save","description":"Only a draft can still change."},{"type":"object","properties":{"action":{"const":"campaign.schedule"},"id":{"type":"integer"},"confirmed":{"type":"boolean"},"now":{"type":"boolean"},"scheduled_at":{"type":"string","format":"date-time"}},"required":["action","id","confirmed"],"title":"campaign.schedule"},{"type":"object","properties":{"action":{"const":"campaign.cancel"},"id":{"type":"integer"}},"required":["action","id"],"title":"campaign.cancel"},{"type":"object","properties":{"action":{"const":"campaign.delete"},"id":{"type":"integer"}},"required":["action","id"],"title":"campaign.delete"},{"type":"object","properties":{"action":{"const":"campaign.preview"},"id":{"type":"integer"}},"required":["action","id"],"title":"campaign.preview"},{"type":"object","properties":{"action":{"const":"campaign.test"},"id":{"type":"integer"},"confirmed":{"type":"boolean"},"variant":{"type":"string","enum":["a","b"]}},"required":["action","id","confirmed"],"title":"campaign.test"},{"type":"object","properties":{"action":{"const":"settings"},"attribution":{"type":"boolean","description":"Click attribution on or off."}},"required":["action"],"title":"settings"}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true},"count":{"type":"integer"},"subscribed":{"type":"integer"},"sample":{"type":"array","items":{"type":"object"}}},"title":"segment.preview"},{"type":"object","properties":{"ok":{"const":true},"id":{"type":"integer"}},"title":"segment.save"},{"type":"object","properties":{"ok":{"const":true}},"title":"segment.delete"},{"type":"object","properties":{"ok":{"const":true},"id":{"type":"integer"},"audience":{"type":"integer"}},"title":"campaign.save"},{"type":"object","properties":{"ok":{"const":true},"campaign":{"type":"object"}},"title":"campaign.schedule"},{"type":"object","properties":{"ok":{"const":true}},"title":"campaign.cancel"},{"type":"object","properties":{"ok":{"const":true}},"title":"campaign.delete"},{"type":"object","properties":{"ok":{"const":true},"subject_a":{"type":"string"},"subject_b":{"type":["string","null"]},"html":{"type":"string"}},"title":"campaign.preview"},{"type":"object","properties":{"ok":{"const":true},"sent_to":{"type":"string","format":"email"}},"title":"campaign.test"},{"type":"object","properties":{"ok":{"const":true}},"title":"settings"}]}}}},"401":{"$ref":"#/components/responses/401"},"403":{"$ref":"#/components/responses/403"},"404":{"$ref":"#/components/responses/404"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/events":{"post":{"tags":["Server to server"],"operationId":"post_events","summary":"Conversion event from your own backend","description":"The project follows from the key. `event_id` is a UUID v4 and must equal the `Idempotency-Key` header; sending the same event again returns `200` with `duplicate: true`. At most 8 kB.\n\n**Access:** The source's conversion key: `Authorization: Bearer <key>`","security":[{"conversionToken":[]}],"x-studio-auth":"conversion","parameters":[{"name":"Idempotency-Key","in":"header","required":true,"schema":{"type":"string"},"description":"Equal to event_id."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"event_id":{"type":"string"},"event":{"type":"string","enum":["website_visited","signup_started","account_created","email_verified","onboarding_completed","activated","subscription_started","payment_succeeded","subscription_canceled"]},"occurred_at":{"type":"string","format":"date-time","description":"ISO 8601; no more than 5 minutes in the future."},"account_ref":{"type":"string","description":"UUID, not an email address."},"visitor_id":{"type":"string"},"session_id":{"type":"string"},"properties":{"type":"object","description":"source, plan, amount (as \"25.00\"), currency."},"attribution":{"type":"object","description":"landing_page, referrer, utm_source, utm_medium, utm_campaign, utm_term, utm_content, gclid, fbclid, msclkid, studio_ref."}},"required":["event_id","event","occurred_at"]},"example":{"event_id":"0b6f1c1e-6f0a-4c51-9d2b-3c1f6f7a8e90","event":"payment_succeeded","occurred_at":"2026-10-05T14:03:00+02:00","account_ref":"6f1d2c3b-1a2b-4c3d-8e9f-0a1b2c3d4e5f","properties":{"plan":"pro","amount":"25.00","currency":"EUR"}}}}},"responses":{"201":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"duplicate":{"type":"boolean"}}},"example":{"ok":true,"duplicate":false}}}},"401":{"$ref":"#/components/responses/401"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/deploys":{"post":{"tags":["Server to server"],"operationId":"post_deploys","summary":"Deploy on the timeline","description":"The same commit twice is one event; only a new deploy starts automations.\n\n**Access:** Deploy key: `Authorization: Bearer <key>`","security":[{"deployToken":[]}],"x-studio-auth":"deploy","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string"},"commit":{"type":"string"},"url":{"type":"string","format":"uri"}}}}}},"responses":{"201":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true}}}}}},"401":{"$ref":"#/components/responses/401"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/mcp":{"post":{"tags":["Server to server"],"operationId":"post_mcp","summary":"MCP server for Claude and other assistants","description":"JSON-RPC 2.0 over HTTP (Streamable HTTP without SSE), read-only: `initialize`, `ping`, `tools/list`, `tools/call`. Tools: projecten, stand_van_project, adviezen, belangrijkste_acties, zoektermen. Any personal key works, including read or write. Connect with: `claude mcp add --transport http studio https://instnt.studio/api/mcp --header \"Authorization: Bearer <key>\"`.\n\n**Access:** Any personal API key (MCP, read or write)","security":[{"apiKey":[]}],"x-studio-auth":"apitoken","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"jsonrpc":{"type":"string"},"id":{"type":"integer"},"method":{"type":"string"},"params":{"type":"object"}},"required":["jsonrpc","method"]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"jsonrpc":{"type":"string"},"id":{"type":"integer"},"result":{"type":"object"},"error":{"type":"object"}}}}}},"401":{"$ref":"#/components/responses/401"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/hooks/appstore/{token}":{"post":{"tags":["Server to server"],"operationId":"post_hooks_appstore_token","summary":"App Store Connect notification (webhook)","description":"With the header `x-apple-signature: hmacsha256=<hex>` (HMAC-SHA256 of the body with the secret). Studio then fetches the app again right away, at most once a minute per account. Wrong signature 401, unknown address 404.\n\n**Access:** No sign-in; the secret is in the address","security":[],"x-studio-auth":"url","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","description":"Apple's payload."}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"received":{"type":"boolean"}}}}}},"404":{"$ref":"#/components/responses/404"},"429":{"$ref":"#/components/responses/429"}}}},"/api/hooks/{token}":{"post":{"tags":["Server to server"],"operationId":"post_hooks_token","summary":"Incoming webhook for an automation","description":"The address is the secret. Fields from the JSON body (one level deep, at most 50, up to 500 characters) become variables in the actions, such as `{{klant}}`. An unknown or disabled address returns 404.\n\n**Access:** No sign-in; the secret is in the address","security":[],"x-studio-auth":"url","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","description":"Free-form fields."}}}},"responses":{"202":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"run":{"type":"boolean"}}}}}},"404":{"$ref":"#/components/responses/404"},"429":{"$ref":"#/components/responses/429"}}}},"/api/inbound/{token}":{"post":{"tags":["Server to server"],"operationId":"post_inbound_token","summary":"Form, booking, call tracking or GitHub as lead or timeline","description":"With a secret Studio verifies the signature: Typeform-Signature, Calendly-Webhook-Signature, X-Hub-Signature-256 (GitHub) or else the X-Studio-Secret header. GitHub without a secret is refused.\n\n**Access:** No sign-in; the secret is in the address","security":[],"x-studio-auth":"url","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","description":"The service's payload."}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"noted":{"type":"boolean"}}}}}},"404":{"$ref":"#/components/responses/404"},"429":{"$ref":"#/components/responses/429"}}}},"/api/backup/{key}":{"get":{"tags":["Server to server"],"operationId":"get_backup_key","summary":"Backup succeeded (ping)","description":"GET or POST, so `curl -fsS` in a cron line is enough. With `size` in bytes Studio also spots a backup that is suddenly much smaller.\n\n**Access:** No sign-in; the secret is in the address","security":[],"x-studio-auth":"url","parameters":[{"name":"key","in":"path","required":true,"schema":{"type":"string"}},{"name":"size","in":"query","required":false,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true}}}}}},"404":{"$ref":"#/components/responses/404"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Server to server"],"operationId":"post_backup_key","summary":"Backup succeeded (ping)","description":"**Access:** No sign-in; the secret is in the address","security":[],"x-studio-auth":"url","parameters":[{"name":"key","in":"path","required":true,"schema":{"type":"string"}},{"name":"size","in":"query","required":false,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true}}}}}},"404":{"$ref":"#/components/responses/404"},"429":{"$ref":"#/components/responses/429"}}}},"/api/titles":{"get":{"tags":["Server to server"],"operationId":"get_titles","summary":"Approved titles to apply","description":"For the WordPress or Drupal module.\n\n**Access:** Write-back key: `Authorization: Bearer wb_…`","security":[{"writebackToken":[]}],"x-studio-auth":"writeback","responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"titles":{"type":"array","items":{"type":"object"},"description":"id, url, title, description"}}}}}},"401":{"$ref":"#/components/responses/401"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}},"post":{"tags":["Server to server"],"operationId":"post_titles","summary":"Titles applied","description":"**Access:** Write-back key: `Authorization: Bearer wb_…`","security":[{"writebackToken":[]}],"x-studio-auth":"writeback","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"applied":{"type":"array","items":{"type":"integer"}}},"required":["applied"]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"applied":{"type":"integer"}}}}}},"401":{"$ref":"#/components/responses/401"},"422":{"$ref":"#/components/responses/422"},"429":{"$ref":"#/components/responses/429"}}}},"/api/errors/{key}":{"post":{"tags":["Server to server"],"operationId":"post_errors_key","summary":"JavaScript error from the site script","description":"The key is public (it is in the HTML). No personal data: no IP, no query strings. Always answers 204.\n\n**Access:** No sign-in","security":[],"x-studio-auth":"public","parameters":[{"name":"key","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"},"source":{"type":"string"},"line":{"type":"integer"},"col":{"type":"integer"},"stack":{"type":"string"},"page":{"type":"string"}},"required":["message"]}}}},"responses":{"204":{"description":"Empty response."}}}},"/api/vitals/{key}":{"post":{"tags":["Server to server"],"operationId":"post_vitals_key","summary":"Web Vitals measurement from the site script","description":"**Access:** No sign-in","security":[],"x-studio-auth":"public","parameters":[{"name":"key","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"lcp":{"type":"number"},"inp":{"type":"number"},"cls":{"type":"number"},"page":{"type":"string"},"device":{"type":"string","enum":["mobile","desktop"]}}}}}},"responses":{"204":{"description":"Empty response."}}}},"/api/forms/{key}":{"post":{"tags":["Server to server"],"operationId":"post_forms_key","summary":"Form or lead click from the site script","description":"Field names only, never what was entered. Or `lead` (telefoon, email, whatsapp) for a link click.\n\n**Access:** No sign-in","security":[],"x-studio-auth":"public","parameters":[{"name":"key","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"page":{"type":"string"},"form":{"type":"string"},"fields":{"type":"array","items":{"type":"string"}},"submitted":{"type":"boolean"},"lead":{"type":"string","enum":["telefoon","email","whatsapp"]},"sr":{"type":"string"}}}}}},"responses":{"204":{"description":"Empty response."}}}},"/api/pageviews/{key}":{"post":{"tags":["Server to server"],"operationId":"post_pageviews_key","summary":"Cookieless pageview","description":"No cookies or storage; a visitor is recognisable for one day through a hash with a daily rotating salt. Bots don't count.\n\n**Access:** No sign-in","security":[],"x-studio-auth":"public","parameters":[{"name":"key","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"page":{"type":"string"},"ref":{"type":"string","format":"uri"},"qref":{"type":"string","description":"Value of ?ref= or ?source=."},"utm_source":{"type":"string"},"utm_medium":{"type":"string"},"utm_campaign":{"type":"string"},"gclid":{"type":"string"},"sr":{"type":"string"}},"required":["page"]}}}},"responses":{"204":{"description":"Empty response."}}}},"/api/site-events/{token}":{"post":{"tags":["Server to server"],"operationId":"post_site_events_token","summary":"Receive a site event","description":"Signed with the project secret: X-Studio-Timestamp (Unix time, at most 5 minutes off) and X-Studio-Signature = sha256=<hex hmac_sha256(secret, timestamp + \".\" + body)>. Studio may store and count `data` (per `ref`); `private` is only used while handling. With `id` a repeated event does not count twice. 502 when an action failed and Studio does not retry it itself: retry later.\n\n**Access:** No sign-in","security":[],"x-studio-auth":"public","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"event":{"type":"string","description":"Lowercase, digits and . _ -, e.g. tester.signed_up."},"id":{"type":"string"},"data":{"type":"object"},"private":{"type":"object"}},"required":["event"]}}}},"responses":{"202":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"runs":{"type":"integer"},"duplicate":{"type":"boolean"}}}}}}}}},"/api/consent/{key}":{"post":{"tags":["Server to server"],"operationId":"post_consent_key","summary":"Cookie banner choice","description":"From the Consent Mode update the banner passes to Google. One choice per visitor per day, using the same daily hash as pageviews; a later choice replaces the earlier one.\n\n**Access:** No sign-in","security":[],"x-studio-auth":"public","parameters":[{"name":"key","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"a":{"type":"string","enum":["granted","denied"],"description":"analytics_storage"},"d":{"type":"string","enum":["granted","denied"],"description":"ad_storage"}}}}}},"responses":{"204":{"description":"Empty response."}}}},"/api/site-script/{key}.js":{"get":{"tags":["Server to server"],"operationId":"get_site_script_key_js","summary":"The site script","description":"Errors, Web Vitals, forms, leads and cookieless measurement in one file. Include `<script src=\"…/api/site-script/<key>.js\" defer></script>`.\n\n**Access:** No sign-in","security":[],"x-studio-auth":"public","parameters":[{"name":"key","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Success.","content":{"application/javascript":{"schema":{"type":"string"}}}}}}},"/api/chat-widget.js":{"get":{"tags":["Server to server"],"operationId":"get_chat_widget_js","summary":"The chat widget","description":"Include `<script src=\"…/api/chat-widget.js?p=<widget-token>\" defer></script>`. The widget only shows with the Chat module on and from an allowed domain.\n\n**Access:** No sign-in","security":[],"x-studio-auth":"public","parameters":[{"name":"p","in":"query","required":true,"schema":{"type":"string"},"description":"Widget token from the chat settings."}],"responses":{"200":{"description":"Success.","content":{"application/javascript":{"schema":{"type":"string"}}}}}}},"/api/chat-widget-home.js":{"get":{"tags":["Server to server"],"operationId":"get_chat_widget_home_js","summary":"The chat widget start screen","description":"Loaded by the widget itself when the project uses self-service, own links or other contact options.\n\n**Access:** No sign-in","security":[],"x-studio-auth":"public","responses":{"200":{"description":"Success.","content":{"application/javascript":{"schema":{"type":"string"}}}}}}},"/api/chat-widget/{token}/beeld/{kind}":{"get":{"tags":["Server to server"],"operationId":"get_chat_widget_token_beeld_kind","summary":"Logo or team photo for the window","description":"kind: logo (if the header shows the logo) or team-<id> (only with \"first name with photo\" and only for that project's team). Otherwise 404.\n\n**Access:** No sign-in","security":[],"x-studio-auth":"public","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string"}},{"name":"kind","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Success.","content":{"application/octet-stream":{"schema":{"type":"string","format":"binary"}}}}}}},"/api/chat-widget/{token}":{"post":{"tags":["Server to server"],"operationId":"post_chat_widget_token","summary":"The chat widget: visitor messages","description":"For the widget itself. JSON as `text/plain`, so there is no preflight. Only with an `Origin` of a project domain (or an added origin); otherwise 404. `visitor` is a random id from the browser (20–64 characters); Studio stores only an HMAC of it and no IP address. The `website` field is a honeypot. At most 15 messages per minute per visitor and 10 new conversations per hour per IP.\n\n**Access:** No sign-in","security":[],"x-studio-auth":"public","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"text/plain":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["hallo","poll","stuur","gegevens","beoordeel","transcript","nieuw","vergeet","geschiedenis","gesprek","kies","koppel","bevestig","identificeer","uitloggen"]},"visitor":{"type":"string"},"since":{"type":"integer","description":"Only messages after this id."},"body":{"type":"string","description":"With stuur; at most 2,000 characters."},"name":{"type":"string"},"email":{"type":"string","format":"email"},"page":{"type":"string","format":"uri"},"title":{"type":"string"},"typing":{"type":"boolean"},"rating":{"type":"integer","description":"With beoordeel: 1–5, only after closing."},"website":{"type":"string","description":"Leave empty."},"topic":{"type":"string","description":"For a new conversation: a topic from the settings; becomes a tag."},"consent":{"type":"boolean","description":"Agreed to the privacy statement, if the form asks for it."},"seen":{"type":"integer","description":"With poll: the last message id the visitor saw in the open, visible window; for \"Read\" in the inbox. Never goes back."},"route":{"type":"array","items":{"type":"object"},"description":"With the first stuur: the pages the visitor viewed in the last half hour (p = path, t = title, at = time in ms), at most ten; until then they stayed in their browser."},"proactive":{"type":"string","description":"The text of the proactive message the visitor replies to; only if it is in the rules."},"id":{"type":"integer","description":"With gesprek: an earlier conversation of the same visitor. With kies: the message with the buttons."},"label":{"type":"string","description":"With kies: one of the buttons under that message; once per message, and is sent as the visitor's message."},"code":{"type":"string","description":"With bevestig: the eight digits from the email after koppel, only valid in the same browser (visitor), for fifteen minutes and once; five wrong attempts void it. This browser then belongs to the address. Until 11-10-2026 a link that linked the requester to whoever clicked."},"user_id":{"type":"string","description":"With identificeer: the id of the signed-in user on the site."},"signature":{"type":"string","description":"With identificeer: HMAC-SHA256 of user_id with the key from the chat settings, computed on the site's server."},"email_signature":{"type":"string","description":"With identificeer: HMAC-SHA256 of \"user_id|address\" (address lower-cased). Only then does `email` count as verified. `uitloggen` undoes this browser's link."},"file":{"type":"string","format":"binary","description":"With stuur as multipart (payload = the JSON above): image or PDF, at most 10 MB, ten per hour."},"selfhelp":{"type":"object","description":"For a new conversation: articles (ids the visitor read), searched, unhelpful (id of an article marked \"No, didn't help\"). Only published articles of this project count; the team sees it as a note."}},"required":["action","visitor"]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"enabled":{"type":"boolean"},"config":{"type":"object","description":"name, color, position, widget (de instellingen van het venster, teksten in beide talen, contactmogelijkheden als link), greeting, buttons (onder de groet), offline_message, prechat, proactive, files, known, status, privacy (null bij white-label zonder eigen verklaring), identify"},"status":{"type":"string","enum":["online","open","gesloten"]},"conversation":{"type":["object","null"],"description":"status, name, email, rated, ticket, agent_typing, read (tot welk bericht het team las), files (mag de bezoeker bijlagen sturen), agent (name, initials, photo, of null), queue (plek in de rij zolang het team nog niet antwoordde)"},"messages":{"type":"array","items":{"type":"object"},"description":"id, from, body, at, name, files, buttons, choice, faq (id, title van een gepubliceerd artikel)"},"history":{"type":"array","items":{"type":"object"},"description":"With geschiedenis: id, at, last_at, status, ticket, preview."},"error":{"type":"string","description":"On 403: files_not_allowed when the team did not allow attachments. On 422: validation, email, unverified (transcript: confirm the address first with koppel/bevestig), code, file (with detail) or prechat (with field: name, email, topic or consent)."}}}}}}}}},"/api/chat-file/{id}":{"get":{"tags":["Server to server"],"operationId":"get_chat_file_id","summary":"Chat attachment","description":"Only with the signed URL from `files` (`expires` and `signature`), valid for half an hour; otherwise 403. Images inline, a PDF as a download, always with `nosniff`.\n\n**Access:** No sign-in","security":[],"x-studio-auth":"public","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Success.","content":{"application/octet-stream":{"schema":{"type":"string","format":"binary"}}}}}}},"/api/chat-widget/{token}/faq":{"post":{"tags":["Server to server"],"operationId":"post_chat_widget_token_faq","summary":"The chat widget: frequently asked questions","description":"For the widget itself, with the same origin rules as `chat-widget/{token}`. Only published articles of that project; the page's language first, otherwise all. With the module or widget off `enabled: false`. At most 60 requests per minute per visitor.\n\n**Access:** No sign-in","security":[],"x-studio-auth":"public","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"text/plain":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["lijst","zoek","categorie","artikel","feedback"]},"q":{"type":"string","description":"With zoek; at most 120 characters. Without results Studio keeps the term without the visitor, for 90 days (can be turned off)."},"lang":{"type":"string","enum":["nl","en"]},"id":{"type":"integer","description":"With artikel and feedback."},"name":{"type":"string","description":"With categorie."},"helpful":{"type":"boolean","description":"With feedback: \"Did this help?\". One vote per article per day per IP hash."}},"required":["action"]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"enabled":{"type":"boolean"},"total":{"type":"integer"},"results":{"type":"array","items":{"type":"object"},"description":"id, title, category, excerpt (bij lijst eerst de uitgelichte)"},"categories":{"type":"array","items":{"type":"object"},"description":"name, count (bij lijst)"},"article":{"type":"object","description":"id, title, category, html, updated_at"}}}}}}}}},"/api/optin/{formKey}":{"post":{"tags":["Server to server"],"operationId":"post_optin_formKey","summary":"Sign up through a form on the client's site","description":"JSON or a plain HTML form. Sends a confirmation email (double opt-in). The `website` field is a honeypot. At most 5 per minute per visitor.\n\n**Access:** No sign-in","security":[],"x-studio-auth":"public","parameters":[{"name":"formKey","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"type":"object","properties":{"email":{"type":"string","format":"email"},"naam":{"type":"string"},"toestemming":{"type":"string","description":"1, on, true or ja."},"taal":{"type":"string","enum":["nl","en"],"description":"Language of the sign-up; without it, the form's language."},"website":{"type":"string","description":"Leave empty."}},"required":["email","toestemming"]}},"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","format":"email"},"naam":{"type":"string"},"toestemming":{"type":"string","description":"1, on, true or ja."},"taal":{"type":"string","enum":["nl","en"],"description":"Language of the sign-up; without it, the form's language."},"website":{"type":"string","description":"Leave empty."}},"required":["email","toestemming"]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"message":{"type":"string"},"error":{"type":"string"}}}}}}}}},"/api/shared":{"get":{"tags":["Public"],"operationId":"get_shared","summary":"Shared report","description":"**Access:** No sign-in; the secret is in the address","security":[],"x-studio-auth":"url","parameters":[{"name":"token","in":"query","required":true,"schema":{"type":"string"},"description":"From the share link."}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"report":{"type":"object"},"project":{"type":"string"},"shared":{"type":"boolean"},"expires_at":{"type":"string","format":"date-time"},"branding":{"type":"object"}}}}}},"404":{"$ref":"#/components/responses/404"},"429":{"$ref":"#/components/responses/429"}}}},"/api/status":{"get":{"tags":["Public"],"operationId":"get_status","summary":"Public status page of a project","description":"**Access:** No sign-in; the secret is in the address","security":[],"x-studio-auth":"url","parameters":[{"name":"token","in":"query","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"name":{"type":"string"},"monitors":{"type":"array","items":{"type":"object"},"description":"name, state, uptime_30d, incidents"},"maintenance":{"type":"array","items":{"type":"object"}},"branding":{"type":"object"},"checked_at":{"type":"string","format":"date-time"}}}}}},"404":{"$ref":"#/components/responses/404"},"429":{"$ref":"#/components/responses/429"}}}},"/api/public-stats":{"get":{"tags":["Public"],"operationId":"get_public_stats","summary":"Totals across all workspaces","description":"For the marketing site; nothing traceable to a client. Cached for an hour, CORS open.\n\n**Access:** No sign-in","security":[],"x-studio-auth":"public","responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"projects":{"type":"integer"},"pages_crawled":{"type":"integer"},"advice_given":{"type":"integer"},"advice_done":{"type":"integer"},"uptime_checks_30d":{"type":"integer"},"incidents_30d":{"type":"integer"},"updated_at":{"type":"string","format":"date-time"}}}}}}}}},"/api/feed/{token}.csv":{"get":{"tags":["Public"],"operationId":"get_feed_token_csv","summary":"Data feed as CSV","description":"Per day: date, users, sessions, pageviews, conversions, clicks, impressions and position in Google. For =IMPORTDATA in Google Sheets.\n\n**Access:** No sign-in; the secret is in the address","security":[],"x-studio-auth":"url","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string"}},{"name":"days","in":"query","required":false,"schema":{"type":"integer"},"description":"7 to 480; defaults to 90."}],"responses":{"200":{"description":"Success.","content":{"text/csv":{"schema":{"type":"string"}}}},"404":{"$ref":"#/components/responses/404"},"429":{"$ref":"#/components/responses/429"}}}},"/api/openapi.json":{"get":{"tags":["Public"],"operationId":"get_openapi_json","summary":"This reference as OpenAPI 3.1","description":"**Access:** No sign-in","security":[],"x-studio-auth":"public","parameters":[{"name":"lang","in":"query","required":false,"schema":{"type":"string","enum":["nl","en"]},"description":"Defaults to nl."}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true}}}}}}}}},"/api/ses/sns":{"post":{"tags":["Internal"],"operationId":"post_ses_sns","summary":"Amazon SES via SNS (shared account)","description":"Bounces, complaints and delivery. Only messages with a valid SNS signature from the configured topic.\n\n**Access:** Signed by the sender","security":[],"x-studio-auth":"signature","responses":{"200":{"description":"Empty response."}}}},"/api/ses/sns/{token}":{"post":{"tags":["Internal"],"operationId":"post_ses_sns_token","summary":"Amazon SES via SNS (a client's own account)","description":"The token selects the sending account, and with it the topic. Only counts for that account's sends.\n\n**Access:** Signed by the sender","security":[],"x-studio-auth":"signature","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Empty response."}}}},"/api/resend/webhook":{"post":{"tags":["Internal"],"operationId":"post_resend_webhook","summary":"Resend events (shared account)","description":"With a valid Svix signature; timestamps older than five minutes are refused.\n\n**Access:** Signed by the sender","security":[],"x-studio-auth":"signature","responses":{"200":{"description":"Empty response."}}}},"/api/resend/webhook/{token}":{"post":{"tags":["Internal"],"operationId":"post_resend_webhook_token","summary":"Resend events (a client's own account)","description":"**Access:** Signed by the sender","security":[],"x-studio-auth":"signature","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Empty response."}}}},"/api/support/inbound":{"post":{"tags":["Internal"],"operationId":"post_support_inbound","summary":"Mail to support (Resend Inbound)","description":"Mail to <token>@SUPPORT_INBOUND_DOMAIN becomes a ticket, or a reply when the subject contains [#number]. Only from known senders of that workspace, with the module and mail to support on. With a valid Svix signature; otherwise 403. An ignored mail also returns 200, with the reason.\n\n**Access:** Signed by the sender","security":[],"x-studio-auth":"signature","responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"status":{"type":"string","enum":["nieuw","antwoord","genegeerd"]},"ticket_id":{"type":"integer"},"reason":{"type":"string"}}}}}}}}},"/api/slack/interactions/{workspace}":{"post":{"tags":["Internal"],"operationId":"post_slack_interactions_workspace","summary":"Buttons in Slack messages","description":"Signed with the Slack app's signing secret.\n\n**Access:** Signed by the sender","security":[],"x-studio-auth":"signature","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Empty response."}}}},"/api/mollie/webhook":{"post":{"tags":["Internal"],"operationId":"post_mollie_webhook","summary":"Payment status from Mollie","description":"Mollie only sends the payment id; Studio fetches the status from Mollie itself, including refunds and chargebacks. 200 for an unknown id, 503 when fetching from Mollie fails (Mollie then retries).\n\n**Access:** No sign-in","security":[],"x-studio-auth":"public","responses":{"200":{"description":"Empty response."}}}}},"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"Personal API key (studio_…) from Settings → API keys."},"session":{"type":"apiKey","in":"cookie","name":"studio_session","description":"The app's session cookie."},"deployToken":{"type":"http","scheme":"bearer","description":"Deploy key (Timeline → deploy key)."},"conversionToken":{"type":"http","scheme":"bearer","description":"A source's conversion key."},"writebackToken":{"type":"http","scheme":"bearer","description":"A project's write-back key (wb_…)."}},"schemas":{"Error":{"type":"object","required":["ok","error"],"properties":{"ok":{"const":false},"error":{"type":"string"},"detail":{"type":"string"},"fields":{"type":"object","additionalProperties":{"type":"string"}}}}},"responses":{"401":{"description":"Missing or invalid key or session (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"The plan doesn't allow this (`plan_limit`, `plan_feature`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed (`forbidden`, `token_read_only`, `token_not_allowed`, `viewing_as_readonly`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Doesn't exist or isn't visible to this user (`not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflicts with existing data.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"410":{"description":"Expired or already used.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Invalid input (`validation`, `unknown_action`); `detail` and `fields` say what.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests (`too_many_requests`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"An external service failed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"No working connection yet (`no_connections`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}