{"openapi":"3.0.3","info":{"title":"Cavuno v1 REST API","version":"1.0.0","description":"The Cavuno API is organized around REST. Our API has predictable resource-oriented URLs, accepts JSON-encoded request bodies, returns JSON-encoded responses, and uses standard HTTP response codes, authentication, and verbs.\n\n## Base URL\n\nProduction: `https://api.cavuno.com/v1`\n\nAll API requests must be made over HTTPS. Calls made over plain HTTP will fail.\n\n## Authentication\n\nThe Cavuno API uses API keys to authenticate requests. You can view and manage your API keys in the Cavuno dashboard, under Settings → API keys.\n\nCavuno API keys have the prefix `cavuno_live_`. Your API keys carry many privileges, so be sure to keep them secure. Do not share your secret API keys in publicly accessible areas such as GitHub, client-side code, and so forth.\n\nAuthentication to the API is performed via Bearer auth. Provide your API key as the bearer token in the `Authorization` header:\n\n```bash\ncurl https://api.cavuno.com/v1/jobs \\\n  -H \"Authorization: Bearer cavuno_live_a1b2c3d4e5f6g7h8_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\"\n```\n\nAPI requests without authentication will fail.\n\n## Rate limits\n\nThe Cavuno API has rate limits in place to ensure stability and reliability. By default, API keys are limited to 100 requests per minute. If you exceed your limit, requests return an HTTP 429 response with a `Retry-After` header indicating when you can try again.\n\nIf you have a use case that needs a higher limit, contact support.\n\n## Idempotency\n\nThe API supports idempotency for safely retrying requests without accidentally performing the same operation twice. For example, if a request to create a job fails due to a network connection error, you can safely retry the request with the same idempotency key, and we'll guarantee that only one job is created.\n\nTo perform an idempotent request, provide an additional `Idempotency-Key` header on any `POST`, `PATCH`, or `DELETE` request:\n\n```bash\ncurl https://api.cavuno.com/v1/jobs \\\n  -X POST \\\n  -H \"Authorization: Bearer cavuno_live_...\" \\\n  -H \"Idempotency-Key: $(uuidgen)\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{ \"title\": \"Senior Engineer\" }'\n```\n\n## Errors\n\nCavuno uses conventional HTTP response codes to indicate the success or failure of an API request. In general: codes in the `2xx` range indicate success, codes in the `4xx` range indicate an error that failed given the information provided (e.g., a required parameter was omitted), and codes in the `5xx` range indicate an error with Cavuno's servers (these are rare).\n\nError responses include a JSON body with details:\n\n```json\n{\n  \"error\": {\n    \"type\": \"validation_error\",\n    \"message\": \"Field 'title' is required.\",\n    \"param\": \"title\",\n    \"requestId\": \"req_...\"\n  }\n}\n```\n\nWhen contacting support about a failed request, include the `requestId` value — it's the fastest way for us to find your request in our logs.\n"},"servers":[{"url":"https://api.cavuno.com/v1","description":"Production"}],"security":[{"bearerAuth":[]}],"tags":[{"name":"API keys","description":"Manage your API keys."},{"name":"Jobs","description":"Create, list, update, and publish jobs."},{"name":"Usage","description":"Your account's current usage and plan limits."},{"name":"Companies","description":"Create, list, update, and delete companies attached to jobs."},{"name":"Media","description":"Upload and manage logos and other binary assets.","x-internal":true},{"name":"Settings","description":"Read and update your board settings.","x-internal":true},{"name":"Audit logs","description":"Read your account audit-log entries.","x-internal":true},{"name":"Operations","description":"Track long-running asynchronous operations.","x-internal":true},{"name":"OAuth","description":"OAuth 2.0 authorization, token, and revocation endpoints.","x-internal":true},{"name":"Public boards","description":"Read-only endpoints for public job boards.","x-internal":true}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"cavuno_live_<keyId>_<secret>","description":"Use your secret API key to authenticate. Pass it as a Bearer token in the `Authorization` header. You can view and manage your API keys in the Cavuno dashboard."},"boardUserBearer":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Board-user access token from `POST /v1/boards/{identifier}/auth/login`. Expires after 1 hour — rotate via `POST /v1/boards/{identifier}/auth/refresh`."}},"schemas":{"ApiKey":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier for the object."},"object":{"type":"string","enum":["api_key"],"description":"String representing the object's type. Objects of the same type share the same value."},"keyId":{"type":"string","description":"Public key ID component, embedded in the key prefix as `cavuno_live_<keyId>_`."},"name":{"type":"string","description":"Human-readable name for the key, shown in the dashboard."},"prefix":{"type":"string","description":"Public prefix in the form `cavuno_live_<keyId>_`. Safe to log; the full plaintext secret is never returned by the API."},"createdAt":{"type":"number","description":"Time at which the key was created. Unix epoch in milliseconds."},"createdBy":{"type":"string","description":"Identifier of the user who minted the key."},"lastUsedAt":{"type":"number","nullable":true,"description":"Time at which the key was last used to authenticate a request, or `null` if it has never been used. Unix epoch in milliseconds."},"revokedAt":{"type":"number","nullable":true,"description":"Time at which the key was revoked, or `null` if the key is still active. Unix epoch in milliseconds."},"expiresAt":{"type":"number","nullable":true,"description":"Time at which the key expires, or `null` if the key has no expiry. Unix epoch in milliseconds."}},"required":["id","object","keyId","name","prefix","createdAt","createdBy","lastUsedAt","revokedAt","expiresAt"]},"AuditLogEntry":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier for the object."},"object":{"type":"string","enum":["audit_log"],"description":"String representing the object's type. Objects of the same type share the same value."},"accountId":{"type":"string","description":"Identifier of the account the entry belongs to."},"actorType":{"type":"string","enum":["api_key","oauth_token","user_session","system"],"description":"Type of actor that performed the action."},"actorId":{"type":"string","description":"Identifier of the actor that performed the action."},"actorLabel":{"type":"string","description":"Human-readable label for the actor, suitable for display."},"action":{"type":"string","description":"Action that was performed (e.g. `jobs.create`, `companies.update`)."},"resourceType":{"type":"string","description":"Type of resource the action was performed on."},"resourceId":{"type":"string","nullable":true,"description":"Identifier of the resource the action was performed on, or `null` for resource-less actions."},"before":{"type":"string","nullable":true,"description":"Serialized JSON snapshot of the resource before the action, or `null` for create actions."},"after":{"type":"string","nullable":true,"description":"Serialized JSON snapshot of the resource after the action, or `null` for delete actions."},"metadata":{"type":"string","nullable":true,"description":"Serialized JSON of free-form metadata about the action (e.g. truncation byte sizes), or `null` if none was attached."},"ipAddress":{"type":"string","nullable":true,"description":"IP address the request originated from, or `null` if unavailable."},"userAgent":{"type":"string","nullable":true,"description":"User-agent string from the request, or `null` if unavailable."},"requestId":{"type":"string","description":"Identifier of the request that produced the entry. Use this to correlate with platform logs."},"timestamp":{"type":"number","description":"Time at which the audit entry was written. Unix epoch in milliseconds."}},"required":["id","object","accountId","actorType","actorId","actorLabel","action","resourceType","resourceId","before","after","metadata","ipAddress","userAgent","requestId","timestamp"],"x-internal":true},"BlogAuthor":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier for the object. Use this value as the `{id}` path parameter for the author endpoints (e.g. `GET /v1/blog/authors/{id}`)."},"object":{"type":"string","enum":["blog_author"],"description":"String representing the object's type. Objects of the same type share the same value."},"name":{"type":"string","description":"Display name of the author."},"slug":{"type":"string","description":"URL-friendly slug for the author."},"bio":{"type":"string","nullable":true,"description":"Short biography, or `null` if not set."},"email":{"type":"string","nullable":true,"description":"The author's email address, or `null` if not set."},"status":{"type":"string","nullable":true,"description":"Author visibility status, or `null` if not set."},"avatarUrl":{"type":"string","nullable":true,"description":"URL of the author avatar, or `null` if no avatar is set."},"websiteUrl":{"type":"string","nullable":true,"description":"The author's website URL, or `null` if not set."},"twitterUrl":{"type":"string","nullable":true,"description":"The author's X (Twitter) URL, or `null` if not set."},"linkedinUrl":{"type":"string","nullable":true,"description":"The author's LinkedIn URL, or `null` if not set."},"githubUrl":{"type":"string","nullable":true,"description":"The author's GitHub URL, or `null` if not set."},"metaTitle":{"type":"string","nullable":true,"description":"SEO meta title, or `null` if not set."},"metaDescription":{"type":"string","nullable":true,"description":"SEO meta description, or `null` if not set."},"createdAt":{"type":"string","description":"Time at which the author was created. ISO 8601 datetime."},"updatedAt":{"type":"string","nullable":true,"description":"Time at which the author was last updated, or `null` if it has never been updated. ISO 8601 datetime."}},"required":["id","object","name","slug","bio","email","status","avatarUrl","websiteUrl","twitterUrl","linkedinUrl","githubUrl","metaTitle","metaDescription","createdAt","updatedAt"]},"CreateAuthorBody":{"type":"object","properties":{"slug":{"type":"string","minLength":1,"maxLength":120,"pattern":"^[a-z0-9-]+$","description":"URL-friendly slug for the author. Auto-generated from `name` when omitted."},"bio":{"type":"string","maxLength":4000,"description":"Short biography for the author. Sanitized HTML."},"email":{"type":"string","maxLength":320,"format":"email","description":"The author's email address."},"status":{"type":"string","enum":["active","inactive"],"description":"Author visibility status. One of `active` or `inactive`."},"avatarStorageId":{"type":"string","nullable":true,"description":"Storage ID of an uploaded avatar image (from `POST /v1/media/upload`). Pass `null` to clear an existing avatar."},"websiteUrl":{"type":"string","maxLength":500,"description":"The author's personal website URL. Normalized to a canonical URL when stored."},"twitterUrl":{"type":"string","maxLength":500,"description":"The author's X (Twitter) profile URL. Stored as the canonical `https://x.com/<handle>` URL."},"linkedinUrl":{"type":"string","maxLength":500,"description":"The author's LinkedIn profile URL."},"githubUrl":{"type":"string","maxLength":500,"description":"The author's GitHub profile URL."},"metaTitle":{"type":"string","maxLength":150,"description":"SEO meta title for the author page."},"metaDescription":{"type":"string","maxLength":320,"description":"SEO meta description for the author page."},"name":{"type":"string","minLength":1,"maxLength":200,"description":"The author's display name."}},"required":["name"],"additionalProperties":false},"UpdateAuthorBody":{"type":"object","properties":{"slug":{"type":"string","minLength":1,"maxLength":120,"pattern":"^[a-z0-9-]+$","description":"URL-friendly slug for the author. Auto-generated from `name` when omitted."},"bio":{"type":"string","maxLength":4000,"description":"Short biography for the author. Sanitized HTML."},"email":{"type":"string","maxLength":320,"format":"email","description":"The author's email address."},"status":{"type":"string","enum":["active","inactive"],"description":"Author visibility status. One of `active` or `inactive`."},"avatarStorageId":{"type":"string","nullable":true,"description":"Storage ID of an uploaded avatar image (from `POST /v1/media/upload`). Pass `null` to clear an existing avatar."},"websiteUrl":{"type":"string","maxLength":500,"description":"The author's personal website URL. Normalized to a canonical URL when stored."},"twitterUrl":{"type":"string","maxLength":500,"description":"The author's X (Twitter) profile URL. Stored as the canonical `https://x.com/<handle>` URL."},"linkedinUrl":{"type":"string","maxLength":500,"description":"The author's LinkedIn profile URL."},"githubUrl":{"type":"string","maxLength":500,"description":"The author's GitHub profile URL."},"metaTitle":{"type":"string","maxLength":150,"description":"SEO meta title for the author page."},"metaDescription":{"type":"string","maxLength":320,"description":"SEO meta description for the author page."},"name":{"type":"string","minLength":1,"maxLength":200,"description":"The author's display name."}},"additionalProperties":false},"BlogPostSummary":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier for the object. Use this value as the `{id}` path parameter for the blog-post endpoints (e.g. `GET /v1/blog/posts/{id}`)."},"object":{"type":"string","enum":["blog_post"]},"title":{"type":"string"},"slug":{"type":"string"},"status":{"type":"string"},"visibility":{"type":"string"},"type":{"type":"string"},"featured":{"type":"boolean"},"authorIds":{"type":"array","items":{"type":"string"}},"tagIds":{"type":"array","items":{"type":"string"}},"coverUrl":{"type":"string","nullable":true},"ogImageUrl":{"type":"string","nullable":true},"featureImageAlt":{"type":"string","nullable":true},"featureImageCaption":{"type":"string","nullable":true},"customExcerpt":{"type":"string","nullable":true},"readingTimeMin":{"type":"number","nullable":true},"seoTitle":{"type":"string","nullable":true},"seoDescription":{"type":"string","nullable":true},"canonicalUrl":{"type":"string","nullable":true},"publishedAt":{"type":"string","nullable":true},"createdAt":{"type":"string"},"updatedAt":{"type":"string","nullable":true}},"required":["id","object","title","slug","status","visibility","type","featured","authorIds","tagIds","coverUrl","ogImageUrl","featureImageAlt","featureImageCaption","customExcerpt","readingTimeMin","seoTitle","seoDescription","canonicalUrl","publishedAt","createdAt","updatedAt"]},"BlogPost":{"allOf":[{"$ref":"#/components/schemas/BlogPostSummary"},{"type":"object","properties":{"html":{"type":"string","nullable":true}},"required":["html"]}]},"CreateBlogPostBody":{"type":"object","properties":{"slug":{"type":"string","minLength":1,"maxLength":120,"pattern":"^[a-z0-9-]+$"},"html":{"type":"string","maxLength":1048576},"customExcerpt":{"type":"string","maxLength":500},"readingTimeMin":{"type":"integer","minimum":0,"maximum":1000},"featured":{"type":"boolean"},"authorIds":{"type":"array","items":{"type":"string","minLength":1},"maxItems":10},"tagIds":{"type":"array","items":{"type":"string","minLength":1},"maxItems":20},"coverStorageId":{"type":"string","nullable":true,"minLength":1},"ogImageStorageId":{"type":"string","nullable":true,"minLength":1},"featureImageAlt":{"type":"string","maxLength":200},"featureImageCaption":{"type":"string","maxLength":500},"seoTitle":{"type":"string","maxLength":150},"seoDescription":{"type":"string","maxLength":320},"canonicalUrl":{"type":"string","maxLength":500,"format":"uri"},"publishedAt":{"type":"string","format":"date-time"},"title":{"type":"string","minLength":1,"maxLength":200},"status":{"type":"string","enum":["draft","scheduled","published"]}},"required":["title"],"additionalProperties":false},"UpdateBlogPostBody":{"type":"object","properties":{"slug":{"type":"string","minLength":1,"maxLength":120,"pattern":"^[a-z0-9-]+$"},"html":{"type":"string","maxLength":1048576},"customExcerpt":{"type":"string","maxLength":500},"readingTimeMin":{"type":"integer","minimum":0,"maximum":1000},"featured":{"type":"boolean"},"authorIds":{"type":"array","items":{"type":"string","minLength":1},"maxItems":10},"tagIds":{"type":"array","items":{"type":"string","minLength":1},"maxItems":20},"coverStorageId":{"type":"string","nullable":true,"minLength":1},"ogImageStorageId":{"type":"string","nullable":true,"minLength":1},"featureImageAlt":{"type":"string","maxLength":200},"featureImageCaption":{"type":"string","maxLength":500},"seoTitle":{"type":"string","maxLength":150},"seoDescription":{"type":"string","maxLength":320},"canonicalUrl":{"type":"string","maxLength":500,"format":"uri"},"publishedAt":{"type":"string","format":"date-time"},"title":{"type":"string","minLength":1,"maxLength":200},"status":{"type":"string","enum":["draft","scheduled","published"]}},"additionalProperties":false},"SearchBlogPostsBody":{"type":"object","properties":{"query":{"type":"string","maxLength":200},"status":{"type":"string","enum":["draft","scheduled","published"]},"cursor":{"type":"string","nullable":true,"minLength":1},"limit":{"type":"integer","minimum":1,"maximum":100}},"additionalProperties":false},"BlogPostsBatchRequest":{"type":"object","properties":{"operations":{"type":"array","items":{"oneOf":[{"type":"object","properties":{"id":{"type":"string","minLength":1},"method":{"type":"string","enum":["POST"]},"body":{"nullable":true},"action":{"type":"string"},"resourceId":{"type":"string"}},"required":["id","method"]},{"type":"object","properties":{"id":{"type":"string","minLength":1},"method":{"type":"string","enum":["PATCH"]},"body":{"nullable":true},"resourceId":{"type":"string","minLength":1}},"required":["id","method","resourceId"]},{"type":"object","properties":{"id":{"type":"string","minLength":1},"method":{"type":"string","enum":["DELETE"]},"resourceId":{"type":"string","minLength":1}},"required":["id","method","resourceId"]}]},"minItems":1,"maxItems":50,"description":"Array of post sub-operations to execute. Each runs independently and reports on the matching entry of the response `data` array. `id` values must be unique. Supported: POST (create), PATCH (update), DELETE, and POST with action `publish`/`unpublish`. Up to 50 entries."}},"required":["operations"],"additionalProperties":false},"BlogTag":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier for the object. Use this value as the `{id}` path parameter for the tag endpoints (e.g. `GET /v1/blog/tags/{id}`)."},"object":{"type":"string","enum":["blog_tag"],"description":"String representing the object's type. Objects of the same type share the same value."},"name":{"type":"string","description":"Display name of the tag."},"slug":{"type":"string","description":"URL-friendly slug for the tag."},"description":{"type":"string","nullable":true,"description":"Tag description, or `null` if not set."},"visibility":{"type":"string","nullable":true,"description":"Tag visibility, or `null` if not set."},"metaTitle":{"type":"string","nullable":true,"description":"SEO meta title, or `null` if not set."},"metaDescription":{"type":"string","nullable":true,"description":"SEO meta description, or `null` if not set."},"createdAt":{"type":"string","description":"Time at which the tag was created. ISO 8601 datetime."},"updatedAt":{"type":"string","nullable":true,"description":"Time at which the tag was last updated, or `null` if it has never been updated. ISO 8601 datetime."}},"required":["id","object","name","slug","description","visibility","metaTitle","metaDescription","createdAt","updatedAt"]},"CreateTagBody":{"type":"object","properties":{"slug":{"type":"string","minLength":1,"maxLength":120,"pattern":"^[a-z0-9-]+$","description":"URL-friendly slug for the tag. Auto-generated from `name` when omitted."},"description":{"type":"string","maxLength":1000,"description":"Description for the tag. Sanitized HTML."},"visibility":{"type":"string","enum":["public","internal"],"description":"Tag visibility. One of `public` or `internal`."},"metaTitle":{"type":"string","maxLength":150,"description":"SEO meta title for the tag page."},"metaDescription":{"type":"string","maxLength":320,"description":"SEO meta description for the tag page."},"name":{"type":"string","minLength":1,"maxLength":200,"description":"The display name of the tag."}},"required":["name"],"additionalProperties":false},"UpdateTagBody":{"type":"object","properties":{"slug":{"type":"string","minLength":1,"maxLength":120,"pattern":"^[a-z0-9-]+$","description":"URL-friendly slug for the tag. Auto-generated from `name` when omitted."},"description":{"type":"string","maxLength":1000,"description":"Description for the tag. Sanitized HTML."},"visibility":{"type":"string","enum":["public","internal"],"description":"Tag visibility. One of `public` or `internal`."},"metaTitle":{"type":"string","maxLength":150,"description":"SEO meta title for the tag page."},"metaDescription":{"type":"string","maxLength":320,"description":"SEO meta description for the tag page."},"name":{"type":"string","minLength":1,"maxLength":200,"description":"The display name of the tag."}},"additionalProperties":false},"PublicBlogAuthorEmbed":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"slug":{"type":"string"},"bio":{"type":"string","nullable":true},"avatarUrl":{"type":"string","nullable":true},"websiteUrl":{"type":"string","nullable":true},"twitterUrl":{"type":"string","nullable":true},"linkedinUrl":{"type":"string","nullable":true},"githubUrl":{"type":"string","nullable":true}},"required":["id","name","slug","bio","avatarUrl","websiteUrl","twitterUrl","linkedinUrl","githubUrl"]},"PublicBlogTagEmbed":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"slug":{"type":"string"},"description":{"type":"string","nullable":true}},"required":["id","name","slug","description"]},"PublicBlogPostSummary":{"type":"object","properties":{"id":{"type":"string"},"object":{"type":"string","enum":["public_blog_post"]},"title":{"type":"string"},"slug":{"type":"string"},"featured":{"type":"boolean"},"coverUrl":{"type":"string","nullable":true},"customExcerpt":{"type":"string","nullable":true},"readingTimeMin":{"type":"number","nullable":true},"publishedAt":{"type":"string","nullable":true},"createdAt":{"type":"string"},"authors":{"type":"array","items":{"$ref":"#/components/schemas/PublicBlogAuthorEmbed"}},"tags":{"type":"array","items":{"$ref":"#/components/schemas/PublicBlogTagEmbed"}}},"required":["id","object","title","slug","featured","coverUrl","customExcerpt","readingTimeMin","publishedAt","createdAt","authors","tags"]},"PublicBlogPost":{"allOf":[{"$ref":"#/components/schemas/PublicBlogPostSummary"},{"type":"object","properties":{"html":{"type":"string","nullable":true},"ogImageUrl":{"type":"string","nullable":true},"featureImageAlt":{"type":"string","nullable":true},"featureImageCaption":{"type":"string","nullable":true},"seoTitle":{"type":"string","nullable":true},"seoDescription":{"type":"string","nullable":true},"canonicalUrl":{"type":"string","nullable":true}},"required":["html","ogImageUrl","featureImageAlt","featureImageCaption","seoTitle","seoDescription","canonicalUrl"]}]},"PublicBlogSearchBody":{"type":"object","properties":{"query":{"type":"string","minLength":1,"maxLength":200,"description":"Free-text search query. Up to 200 characters."},"cursor":{"type":"string","nullable":true,"minLength":1,"description":"An opaque pagination cursor returned in the `nextCursor` field of a previous response. Pass it back to fetch the next page of results."},"limit":{"type":"integer","minimum":1,"maximum":50,"description":"A limit on the number of objects to be returned. Limit can range between 1 and 50."}},"required":["query"],"additionalProperties":false},"PublicBlogTag":{"allOf":[{"$ref":"#/components/schemas/PublicBlogTagEmbed"},{"type":"object","properties":{"object":{"type":"string","enum":["public_blog_tag"]}},"required":["object"]}]},"PublicBlogAuthor":{"allOf":[{"$ref":"#/components/schemas/PublicBlogAuthorEmbed"},{"type":"object","properties":{"object":{"type":"string","enum":["public_blog_author"]}},"required":["object"]}]},"ResourceLinks":{"type":"object","properties":{"public":{"type":"string","format":"uri","description":"Canonical public URL for this resource on the board. Honours the configured custom domain when present, otherwise uses the board subdomain."},"admin":{"type":"string","format":"uri","description":"URL inside the Cavuno app where the authenticated owner can manage this resource."}},"required":["public","admin"]},"CompanySummary":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier for the object. Use this value as the `{id}` path parameter for the company endpoints (e.g. `GET /v1/companies/{id}`)."},"object":{"type":"string","enum":["company"],"description":"String representing the object's type. Objects of the same type share the same value."},"name":{"type":"string","description":"Display name of the company."},"slug":{"type":"string","description":"URL-friendly slug for the company."},"website":{"type":"string","nullable":true,"description":"Public company website URL, or `null` if not set."},"logoUrl":{"type":"string","nullable":true,"description":"URL of the company logo, or `null` if no logo is set."},"summary":{"type":"string","nullable":true,"description":"One-line summary of the company, or `null` if not set."},"xUrl":{"type":"string","nullable":true,"description":"X (Twitter) profile URL, or `null` if not set."},"linkedinUrl":{"type":"string","nullable":true,"description":"LinkedIn page URL, or `null` if not set."},"facebookUrl":{"type":"string","nullable":true,"description":"Facebook page URL, or `null` if not set."},"createdAt":{"type":"string","description":"Time at which the company was created. ISO 8601 datetime."},"updatedAt":{"type":"string","nullable":true,"description":"Time at which the company was last updated, or `null` if it has never been updated. ISO 8601 datetime."},"links":{"allOf":[{"$ref":"#/components/schemas/ResourceLinks"},{"type":"object","properties":{"public":{"type":"string","nullable":true,"format":"uri","description":"Canonical public URL for this company on the board, or `null` when no slug is set."}}}],"description":"Public + admin URLs for this company. `public` may be `null` if the company lacks a slug."}},"required":["id","object","name","slug","website","logoUrl","summary","xUrl","linkedinUrl","facebookUrl","createdAt","updatedAt","links"]},"Company":{"allOf":[{"$ref":"#/components/schemas/CompanySummary"},{"type":"object","properties":{"description":{"type":"string","nullable":true,"description":"Long-form description of the company, or `null` if not set."},"canBackfill":{"type":"boolean","nullable":true,"description":"Whether automated backfill of jobs from this company is supported, or `null` if not yet evaluated."},"jobCount":{"type":"number","description":"Total number of jobs at this company across all statuses."},"publishedJobCount":{"type":"number","description":"Number of currently-published jobs at this company."}},"required":["description","canBackfill","jobCount","publishedJobCount"]}]},"CreateCompanyBody":{"type":"object","properties":{"website":{"type":"string","maxLength":200,"description":"Public company website URL. Normalized to a canonical apex domain when stored."},"summary":{"type":"string","maxLength":280,"description":"One-line summary of the company. Up to 280 characters."},"description":{"type":"string","maxLength":25000,"description":"Long-form description of the company. Up to 25,000 characters."},"xUrl":{"type":"string","maxLength":500,"description":"X (Twitter) profile URL or handle. Stored as the canonical handle."},"linkedinUrl":{"type":"string","maxLength":500,"description":"LinkedIn company page URL."},"facebookUrl":{"type":"string","maxLength":500,"description":"Facebook company page URL."},"name":{"type":"string","minLength":1,"maxLength":120,"description":"The company's display name."},"slug":{"type":"string","minLength":1,"maxLength":150,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$","description":"URL-friendly slug for the company. Auto-generated from `name` when omitted."}},"required":["name"],"additionalProperties":false},"FindOrCreateCompanyBody":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":120,"description":"The company's display name."},"website":{"type":"string","maxLength":200,"description":"Public company website URL. Used to resolve to an existing company by domain before falling back to creation."},"summary":{"type":"string","maxLength":280,"description":"One-line summary of the company. Up to 280 characters."},"matchByName":{"type":"boolean","description":"If `true` (default), falls back to matching by `name` when no website match is found. Set to `false` to match by website domain only."},"createIfMissing":{"type":"boolean","description":"If `true` (default), creates a new company when no match is found. Set to `false` to receive a `404 companies_not_found` response instead."}},"required":["name"],"additionalProperties":false},"UpdateCompanyBody":{"type":"object","properties":{"website":{"type":"string","maxLength":200,"description":"Public company website URL. Normalized to a canonical apex domain when stored."},"summary":{"type":"string","maxLength":280,"description":"One-line summary of the company. Up to 280 characters."},"description":{"type":"string","maxLength":25000,"description":"Long-form description of the company. Up to 25,000 characters."},"xUrl":{"type":"string","maxLength":500,"description":"X (Twitter) profile URL or handle. Stored as the canonical handle."},"linkedinUrl":{"type":"string","maxLength":500,"description":"LinkedIn company page URL."},"facebookUrl":{"type":"string","maxLength":500,"description":"Facebook company page URL."},"name":{"type":"string","minLength":1,"maxLength":120,"description":"The company's display name."},"slug":{"type":"string","minLength":1,"maxLength":150,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$","description":"URL-friendly slug for the company."}},"additionalProperties":false},"SearchCompaniesBody":{"type":"object","properties":{"query":{"type":"string","maxLength":200,"description":"Free-text search query matched against company name. Up to 200 characters."},"cursor":{"type":"string","nullable":true,"minLength":1,"description":"An opaque pagination cursor returned in the `nextCursor` field of a previous response. Pass it back to fetch the next page of results."},"limit":{"type":"integer","minimum":1,"maximum":100,"description":"A limit on the number of objects to be returned. Limit can range between 1 and 100.","example":20}},"additionalProperties":false},"PublicCompanyLinks":{"type":"object","properties":{"public":{"type":"string","nullable":true,"format":"uri","description":"Canonical public URL for this company on the board, or `null` when no slug is set."}},"required":["public"],"description":"Public-only links. The admin URL is intentionally omitted on public-board responses."},"CompanyPublic":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier for the object. Use this value as the `{id}` path parameter for the company endpoints (e.g. `GET /v1/companies/{id}`)."},"object":{"type":"string","enum":["public_company"],"description":"String representing the object's type. Objects of the same type share the same value."},"name":{"type":"string","description":"Display name of the company."},"slug":{"type":"string","description":"URL-friendly slug for the company."},"website":{"type":"string","nullable":true,"description":"Public company website URL, or `null` if not set."},"logoUrl":{"type":"string","nullable":true,"description":"URL of the company logo, or `null` if no logo is set."},"description":{"type":"string","nullable":true,"description":"Long-form description of the company, or `null` if not set."},"jobCount":{"type":"number","description":"Total number of public-facing jobs at this company. Currently mirrors `publishedJobCount`."},"publishedJobCount":{"type":"number","description":"Number of currently-published, non-expired jobs at this company."},"links":{"$ref":"#/components/schemas/PublicCompanyLinks"}},"required":["id","object","name","slug","website","logoUrl","description","jobCount","publishedJobCount","links"]},"PublicCompaniesSearchBody":{"type":"object","properties":{"query":{"type":"string","maxLength":200,"description":"Free-text search query matched against company name. Up to 200 characters."},"cursor":{"type":"string","nullable":true,"minLength":1,"description":"An opaque pagination cursor returned in the `nextCursor` field of a previous response. Pass it back to fetch the next page of results."},"limit":{"type":"integer","minimum":1,"maximum":100,"description":"A limit on the number of objects to be returned. Limit can range between 1 and 100."}},"additionalProperties":false},"AdminOnlyResourceLinks":{"type":"object","properties":{"admin":{"type":"string","format":"uri","description":"URL inside the Cavuno app where the authenticated owner can manage this resource."}},"required":["admin"],"description":"Links related to this resource. List responses only carry the `admin` URL because the public URL needs the company slug, which list-shape Convex projections do not fan out to."},"JobSummary":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier for the object. Use this value as the `{id}` path parameter for the job endpoints (e.g. `GET /v1/jobs/{id}`)."},"object":{"type":"string","enum":["job"],"description":"String representing the object's type. Objects of the same type share the same value."},"title":{"type":"string","description":"The job title."},"slug":{"type":"string","nullable":true,"description":"URL-friendly slug used in public board URLs, or `null` if no slug is set."},"status":{"type":"string","enum":["draft","published","expired","archived"],"description":"Current status of the job. One of `draft`, `published`, `expired`, or `archived`."},"companyId":{"type":"string","nullable":true,"description":"Identifier of the company the job belongs to, or `null` if no company is attached."},"employmentType":{"type":"string","nullable":true,"enum":["full_time","part_time","contract","internship","temporary","volunteer","other"],"description":"Employment type of the role, or `null` if not specified."},"remoteOption":{"type":"string","nullable":true,"enum":["on_site","hybrid","remote"],"description":"Whether the role is on-site, hybrid, or fully remote, or `null` if not specified."},"seniority":{"type":"string","nullable":true,"enum":["entry_level","associate","mid_level","senior","lead","principal","director","executive"],"description":"Seniority level of the role, or `null` if not specified."},"salaryMin":{"type":"number","nullable":true,"description":"Minimum salary in `salaryCurrency` units, or `null` if not specified."},"salaryMax":{"type":"number","nullable":true,"description":"Maximum salary in `salaryCurrency` units, or `null` if not specified."},"salaryCurrency":{"type":"string","nullable":true,"description":"Three-letter ISO 4217 currency code for the salary range, or `null` if no salary is specified."},"salaryTimeframe":{"type":"string","nullable":true,"enum":["per_year","per_month","per_week","per_day","per_hour"],"description":"Period the salary range is quoted against, or `null` if no salary is specified."},"isFeatured":{"type":"boolean","description":"Whether the job appears in featured slots on the public board."},"publishedAt":{"type":"string","nullable":true,"description":"Time at which the job was first published, or `null` if not yet published. ISO 8601 datetime."},"expiresAt":{"type":"string","nullable":true,"description":"Time at which the job expires, or `null` if no expiry is set. ISO 8601 datetime."},"createdAt":{"type":"string","description":"Time at which the job was created. ISO 8601 datetime."},"updatedAt":{"type":"string","description":"Time at which the job was last updated. ISO 8601 datetime."},"externalId":{"type":"string","nullable":true,"description":"External identifier supplied by the caller on create, used for deduplication via `GET /v1/jobs?externalId=...`. `null` when no external identifier was set."},"customFieldValues":{"type":"object","additionalProperties":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}},{"type":"boolean"},{"type":"number"}]},"description":"Board-defined custom-field values for this job, keyed by the field `key` (the definitions are published at `GET /v1/settings/job-form`). Each value is returned as stored: a string (`short_text` / `long_text` / a `single_select` option key), a string array (`multi_select` option keys), a boolean, or a number. Always an object — `{}` when the job has no custom-field values, never `null` or a missing field. Only real values are stored, so this never contains `null` or empty values."},"links":{"$ref":"#/components/schemas/AdminOnlyResourceLinks"}},"required":["id","object","title","slug","status","companyId","employmentType","remoteOption","seniority","salaryMin","salaryMax","salaryCurrency","salaryTimeframe","isFeatured","publishedAt","expiresAt","createdAt","updatedAt","externalId","customFieldValues","links"]},"JobCompany":{"type":"object","nullable":true,"properties":{"id":{"type":"string","description":"Unique identifier for the company."},"name":{"type":"string","nullable":true,"description":"Display name of the company, or `null` if not yet set."},"slug":{"type":"string","nullable":true,"description":"URL slug of the company, or `null` if not yet set."},"logoUrl":{"type":"string","nullable":true,"description":"URL of the company logo, or `null` if no logo is set."},"website":{"type":"string","nullable":true,"description":"Company website URL, or `null` if no website is set."}},"required":["id","name","slug","logoUrl","website"],"description":"Embedded company resource for the job, or `null` if no company is attached."},"JobOfficeLocation":{"type":"object","properties":{"countryCode":{"type":"string","nullable":true,"description":"ISO 3166-1 alpha-2 country code, or `null` if the location was not resolved."},"country":{"type":"string","nullable":true,"description":"Full country name, or `null` if the location was not resolved."},"locality":{"type":"string","nullable":true,"description":"Neighborhood or sub-locality, or `null` if not resolved."},"city":{"type":"string","nullable":true,"description":"City, or `null` if not resolved."},"region":{"type":"string","nullable":true,"description":"Region, state, or province name, or `null` if not resolved."},"regionCode":{"type":"string","nullable":true,"description":"Region or state code (e.g. `CA` for California), or `null` if not resolved."},"displayName":{"type":"string","nullable":true,"description":"Pre-formatted display name for the location, or `null` if not resolved."}},"required":["countryCode","country","locality","city","region","regionCode","displayName"]},"Job":{"allOf":[{"$ref":"#/components/schemas/JobSummary"},{"type":"object","properties":{"links":{"allOf":[{"$ref":"#/components/schemas/ResourceLinks"},{"type":"object","properties":{"public":{"type":"string","nullable":true,"format":"uri","description":"Canonical public URL for this job, or `null` when the job has no slug or no associated company slug (so a stable URL cannot be assembled)."}}}],"description":"Public + admin URLs for this job. `public` may be `null` if the job lacks a slug or company-slug."},"description":{"type":"string","nullable":true,"description":"Long-form description of the role, or `null` if not specified."},"applicationUrl":{"type":"string","nullable":true,"description":"Where candidates apply, or `null` if not specified. An HTTPS URL or `mailto:` URI."},"remotePermits":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string"},"value":{"type":"string"}},"required":["type","value"]},"description":"Hierarchical permit selection authored by the caller. Lossless round-trip with the input — `[{type:\"world_region\",value:\"EMEA\"}]` goes in and comes out the same. The three `remoteWorkPermit*` and `remoteWorldwide` fields below are read-only derived projections of this list."},"remoteWorldwide":{"type":"boolean","nullable":true,"description":"Read-only — derived from `remotePermits`. `true` only when the authored selection is a single `{type:\"worldwide\"}` entry."},"remoteTimezones":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string"},"value":{"type":"string"},"plusMinus":{"type":"number"}},"required":["type","value"]},"description":"Hierarchical timezone selection. Lossless round-trip with the authored input. When omitted on POST, the server auto-derives this from `remotePermits` (or `[{all,all}]` if neither was provided). PATCH never auto-re-derives — once set, only an explicit replacement updates the stored value. The flat `remoteAllowedTzOffsets` field below is the read-only derived projection used by search."},"remoteAllowedTzOffsets":{"type":"array","items":{"type":"number"},"description":"Read-only — derived from `remoteTimezones` (per-country/per-region offset fan-out). UTC hour offsets used by the job-search index."},"remoteWorkPermitCountryCodes":{"type":"array","items":{"type":"string"},"description":"Read-only — derived from `remotePermits` (hierarchical groups fan out to alpha2 sets; subdivisions auto-add the parent country). ISO 3166-1 alpha-2 codes."},"remoteWorkPermitSubdivisionCodes":{"type":"array","items":{"type":"string"},"description":"Read-only — derived from `remotePermits` (subdivision entries only). ISO 3166-2 codes."},"remoteSponsorship":{"type":"string","enum":["yes","no","unknown"],"description":"Whether the employer sponsors visas for remote candidates. One of `yes`, `no`, or `unknown`."},"educationRequirements":{"type":"array","items":{"type":"string","enum":["high_school","associate_degree","bachelor_degree","professional_certificate","postgraduate_degree","no_requirements"]},"description":"Required education credentials. Each value is one of `high_school`, `associate_degree`, `bachelor_degree`, `professional_certificate`, `postgraduate_degree`, or `no_requirements`."},"experienceMonths":{"type":"number","nullable":true,"description":"Minimum required experience in months, or `null` if not specified."},"experienceInPlaceOfEducation":{"type":"boolean","nullable":true,"description":"If `true`, equivalent experience may substitute for the listed education requirements. `null` if not specified."},"inOfficePeriod":{"type":"string","nullable":true,"enum":["per_week","per_month","per_year"],"description":"Period denominator for `inOfficeFrequency`, or `null` if not specified."},"inOfficeFrequency":{"type":"number","nullable":true,"description":"How often the candidate must be in-office over `inOfficePeriod`, or `null` if not specified."},"company":{"$ref":"#/components/schemas/JobCompany"},"officeLocations":{"type":"array","items":{"$ref":"#/components/schemas/JobOfficeLocation"},"description":"Physical office locations associated with the job."}},"required":["description","applicationUrl","remotePermits","remoteWorldwide","remoteTimezones","remoteAllowedTzOffsets","remoteWorkPermitCountryCodes","remoteWorkPermitSubdivisionCodes","remoteSponsorship","educationRequirements","experienceMonths","experienceInPlaceOfEducation","inOfficePeriod","inOfficeFrequency","company","officeLocations"]}]},"JobOfficeLocationInput":{"anyOf":[{"type":"object","properties":{"locality":{"type":"string","minLength":1,"description":"Neighborhood or sub-locality."},"city":{"type":"string","minLength":1,"description":"City."},"region":{"type":"string","minLength":1,"description":"Region, state, or province."},"country":{"type":"string","minLength":1,"description":"ISO 3166-1 alpha-2 country code OR recognized country name/alias. Aliases are normalized to canonical alpha-2 server-side (e.g. `US`, `USA`, `United States` → `US`; `UK`, `GB`, `United Kingdom` → `GB`)."}},"required":["city","country"],"additionalProperties":false},{"type":"object","properties":{"query":{"type":"string","minLength":1,"maxLength":500,"description":"Free-form location string (e.g. `\"Berlin, Germany\"`, `\"Mountain View, California, USA\"`). Mapbox parses + ranks candidates server-side; rejects on low confidence."}},"required":["query"],"additionalProperties":false}]},"InlineCompanyInput":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":120,"description":"The company's display name."},"website":{"type":"string","maxLength":200,"description":"Public company website URL. Used to resolve to an existing company by domain before falling back to creation."},"summary":{"type":"string","maxLength":280,"description":"One-line summary of the company. Up to 280 characters."},"matchByName":{"type":"boolean","description":"If `true` (default), falls back to matching by `name` when no website match is found. Set to `false` to match by website domain only."},"createIfMissing":{"type":"boolean","description":"If `true` (default), creates a new company when no match is found. Set to `false` to receive a `404 companies_not_found` response instead."}},"required":["name"],"additionalProperties":false,"description":"An inline company payload, resolved via `find-or-create` before the job is created. Provide either `company` or `companyId`, not both."},"CreateJobBody":{"type":"object","properties":{"companyId":{"type":"string","minLength":1,"description":"The ID of an existing company. Provide either `companyId` or `company`, not both."},"description":{"type":"string","minLength":1,"maxLength":25000,"description":"Long-form description of the role. Up to 25,000 characters."},"slug":{"type":"string","description":"URL-friendly slug for the job. Auto-generated from `title` when omitted."},"employmentType":{"type":"string","enum":["full_time","part_time","contract","internship","temporary","volunteer","other"],"description":"Employment type of the role."},"remoteOption":{"type":"string","enum":["on_site","hybrid","remote"],"description":"Whether the role is on-site, hybrid, or fully remote."},"remotePermits":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["worldwide","world_region","continent","region","subregion","subdivision","country","custom"]},"value":{"type":"string","minLength":1}},"required":["type","value"],"additionalProperties":false},"description":"Where remote candidates must hold work authorization. Each entry is the smallest relevant scope: `worldwide`, a `world_region` (EMEA / LATAM / NA / APAC), a `continent`, a `region`, a `subregion`, a `custom` group (e.g. `EU`), a `country` (ISO 3166-1 alpha-2), or a `subdivision` (ISO 3166-2). Subdivisions auto-imply their parent country in the derived `remoteWorkPermitCountryCodes` output. Worldwide is mutually exclusive with all other entries. The canonical `{type, value}` set is published at `GET /v1/taxonomies/remote-permits`. Pass `[]` to clear an existing constraint."},"remoteTimezones":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["all","world_region","continent","region","subregion","country","timezone"]},"value":{"type":"string","minLength":1},"plusMinus":{"type":"number","minimum":0,"maximum":12}},"required":["type","value"],"additionalProperties":false},"description":"Where remote candidates must be timezone-compatible. Each entry mirrors the `remotePermits` shape (`world_region`, `continent`, `region`, `subregion`, `country`) plus `timezone` (specific IANA name with optional `plusMinus` ±N hours expansion) and `all` (every timezone — equivalent of `worldwide` for permits). The canonical `{type, value}` set is published at `GET /v1/taxonomies/remote-timezones`. **When omitted on POST**, the server auto-derives this from `remotePermits` (or `[{all,all}]` if neither was provided). **PATCH never auto-re-derives** — once set, only an explicit replacement updates the stored value, even when `remotePermits` changes. Pass `[]` to clear an existing constraint."},"remoteSponsorship":{"type":"string","enum":["yes","no","unknown"],"description":"Whether the employer sponsors visas for remote candidates. One of `yes`, `no`, or `unknown`."},"seniority":{"type":"string","enum":["entry_level","associate","mid_level","senior","lead","principal","director","executive"],"description":"Seniority level of the role."},"applicationUrl":{"type":"string","minLength":1,"maxLength":2048,"description":"Where candidates apply. Accepts an HTTPS URL, a `mailto:` URI, or a bare email address (which is normalized to `mailto:` form)."},"salaryMin":{"type":"number","minimum":0,"description":"Minimum salary, in `salaryCurrency` units."},"salaryMax":{"type":"number","minimum":0,"description":"Maximum salary, in `salaryCurrency` units."},"salaryCurrency":{"type":"string","description":"Three-letter ISO 4217 currency code for `salaryMin` and `salaryMax`."},"salaryTimeframe":{"type":"string","enum":["per_year","per_month","per_week","per_day","per_hour"],"description":"Period the `salaryMin` and `salaryMax` figures are quoted against."},"isFeatured":{"type":"boolean","description":"Whether the job appears in featured slots on the public board."},"expiresAt":{"anyOf":[{"type":"integer","minimum":0},{"nullable":true},{"nullable":true}],"description":"Job expiry as a Unix epoch in milliseconds. On create, omitted or `null` defaults to 30 days from creation. On PATCH, pass `null` to clear an existing expiry. Past timestamps remove the job from the public board."},"publishedAt":{"type":"integer","minimum":0,"description":"Time at which the job was first published, as a Unix epoch in milliseconds. When omitted on create with `status: \"published\"`, the server stamps the current time. Useful for bulk-importing historical jobs while preserving original publication dates. PATCH may overwrite an existing value but cannot clear it."},"educationRequirements":{"type":"array","items":{"type":"string","enum":["high_school","associate_degree","bachelor_degree","professional_certificate","postgraduate_degree","no_requirements"]},"description":"Required education credentials. Each value is one of `high_school`, `associate_degree`, `bachelor_degree`, `professional_certificate`, `postgraduate_degree`, or `no_requirements`."},"experienceMonths":{"type":"integer","minimum":0,"description":"Minimum required experience, expressed in months."},"experienceInPlaceOfEducation":{"type":"boolean","description":"If `true`, equivalent experience may substitute for the listed education requirements."},"inOfficePeriod":{"type":"string","enum":["per_week","per_month","per_year"],"description":"Period denominator for `inOfficeFrequency`."},"inOfficeFrequency":{"type":"number","minimum":0,"description":"How often the candidate must be in-office over `inOfficePeriod`."},"officeLocations":{"type":"array","items":{"$ref":"#/components/schemas/JobOfficeLocationInput"},"description":"Physical office locations associated with the job. Each entry is forward-geocoded server-side; a country mismatch returns `400 jobs_unresolvable_location`."},"externalId":{"type":"string","minLength":1,"maxLength":255,"description":"An external identifier for the job from your own system, such as an ATS requisition ID. Use this value to look up the job later via `GET /v1/jobs?externalId=...` for deduplication. Scoped per-account: two different accounts may reuse the same `externalId` without collision. Up to 255 characters."},"customFieldValues":{"type":"object","additionalProperties":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}},{"type":"boolean"},{"type":"number"},{"nullable":true},{"nullable":true}]},"description":"Board-defined custom-field values, keyed by the field `key` (definitions, including type and option keys, are published at `GET /v1/settings/job-form`). Writes are **additive**: on `PATCH` a key you send is set/overwritten and a key you omit is preserved (unsent keys are never cleared); on `POST` this initializes the bag. Send a key with an intentional-empty value — `null`, `\"\"`, or `[]` — to **clear** it (`\"\"`/`null` clear any type; `[]` clears a `multi_select`); `false` and `0` are kept as real values. Values must match the field type and `single_select`/`multi_select` must use defined option **keys** (not labels); a wrong-typed value is rejected (`custom_field_wrong_type`), never silently cleared. Unknown keys are ignored. The stored bag never contains `null`/empty values."},"title":{"type":"string","minLength":1,"maxLength":200,"description":"The job title."},"company":{"$ref":"#/components/schemas/InlineCompanyInput"},"status":{"type":"string","enum":["draft","published"],"description":"Initial status of the job. Defaults to `draft`. Only `draft` and `published` are writable here. The system-set values `expired` and `archived` are not — use the dedicated transitions (`POST /v1/jobs/:id/publish`, `/pause`, `/expire`) for status changes after create."}},"required":["description","applicationUrl","title"],"additionalProperties":false},"PublicJobLinks":{"type":"object","properties":{"public":{"type":"string","nullable":true,"format":"uri","description":"Canonical public URL for this job, or `null` when the job has no slug or no associated company slug (so a stable URL cannot be assembled)."}},"required":["public"],"description":"Public-only links. The admin URL is intentionally omitted on public-board responses."},"PublicJob":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier for the object. Use this value as the `{id}` path parameter for the job endpoints (e.g. `GET /v1/jobs/{id}`)."},"object":{"type":"string","enum":["public_job"],"description":"String representing the object's type. Objects of the same type share the same value."},"title":{"type":"string","description":"The job title."},"slug":{"type":"string","nullable":true,"description":"URL-friendly slug used in public board URLs, or `null` if no slug is set."},"status":{"type":"string","enum":["draft","published","expired","archived"],"description":"Current status of the job. One of `draft`, `published`, `expired`, or `archived`."},"companyId":{"type":"string","nullable":true,"description":"Identifier of the company the job belongs to, or `null` if no company is attached."},"employmentType":{"type":"string","nullable":true,"enum":["full_time","part_time","contract","internship","temporary","volunteer","other"],"description":"Employment type of the role, or `null` if not specified."},"remoteOption":{"type":"string","nullable":true,"enum":["on_site","hybrid","remote"],"description":"Whether the role is on-site, hybrid, or fully remote, or `null` if not specified."},"seniority":{"type":"string","nullable":true,"enum":["entry_level","associate","mid_level","senior","lead","principal","director","executive"],"description":"Seniority level of the role, or `null` if not specified."},"salaryMin":{"type":"number","nullable":true,"description":"Minimum salary in `salaryCurrency` units, or `null` if not specified."},"salaryMax":{"type":"number","nullable":true,"description":"Maximum salary in `salaryCurrency` units, or `null` if not specified."},"salaryCurrency":{"type":"string","nullable":true,"description":"Three-letter ISO 4217 currency code for the salary range, or `null` if no salary is specified."},"salaryTimeframe":{"type":"string","nullable":true,"enum":["per_year","per_month","per_week","per_day","per_hour"],"description":"Period the salary range is quoted against, or `null` if no salary is specified."},"isFeatured":{"type":"boolean","description":"Whether the job appears in featured slots on the public board."},"publishedAt":{"type":"string","nullable":true,"description":"Time at which the job was first published, or `null` if not yet published. ISO 8601 datetime."},"expiresAt":{"type":"string","nullable":true,"description":"Time at which the job expires, or `null` if no expiry is set. ISO 8601 datetime."},"createdAt":{"type":"string","description":"Time at which the job was created. ISO 8601 datetime."},"updatedAt":{"type":"string","description":"Time at which the job was last updated. ISO 8601 datetime."},"externalId":{"type":"string","nullable":true,"description":"External identifier supplied by the caller on create, used for deduplication via `GET /v1/jobs?externalId=...`. `null` when no external identifier was set."},"links":{"$ref":"#/components/schemas/PublicJobLinks"},"description":{"type":"string","nullable":true,"description":"Long-form description of the role, or `null` if not specified."},"applicationUrl":{"type":"string","nullable":true,"description":"Where candidates apply, or `null` if not specified. An HTTPS URL or `mailto:` URI."},"remotePermits":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string"},"value":{"type":"string"}},"required":["type","value"]},"description":"Hierarchical permit selection authored by the board owner. The three `remoteWorkPermit*` and `remoteWorldwide` fields are read-only derived projections."},"remoteWorldwide":{"type":"boolean","nullable":true,"description":"Read-only — derived from `remotePermits`. `true` only when the authored selection is a single `{type:\"worldwide\"}` entry."},"remoteTimezones":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string"},"value":{"type":"string"},"plusMinus":{"type":"number"}},"required":["type","value"]},"description":"Hierarchical timezone selection. The flat `remoteAllowedTzOffsets` field below is the read-only derived projection."},"remoteAllowedTzOffsets":{"type":"array","items":{"type":"number"},"description":"Read-only — derived from `remoteTimezones`. UTC hour offsets used by the job-search index."},"remoteWorkPermitCountryCodes":{"type":"array","items":{"type":"string"},"description":"Read-only — derived from `remotePermits` (hierarchical groups fan out to alpha2 sets; subdivisions auto-add the parent country). ISO 3166-1 alpha-2 codes."},"remoteWorkPermitSubdivisionCodes":{"type":"array","items":{"type":"string"},"description":"Read-only — derived from `remotePermits` (subdivision entries only). ISO 3166-2 codes."},"remoteSponsorship":{"type":"string","enum":["yes","no","unknown"],"description":"Whether the employer sponsors visas for remote candidates. One of `yes`, `no`, or `unknown`."},"educationRequirements":{"type":"array","items":{"type":"string","enum":["high_school","associate_degree","bachelor_degree","professional_certificate","postgraduate_degree","no_requirements"]},"description":"Required education credentials. Each value is one of `high_school`, `associate_degree`, `bachelor_degree`, `professional_certificate`, `postgraduate_degree`, or `no_requirements`."},"experienceMonths":{"type":"number","nullable":true,"description":"Minimum required experience in months, or `null` if not specified."},"experienceInPlaceOfEducation":{"type":"boolean","nullable":true,"description":"If `true`, equivalent experience may substitute for the listed education requirements. `null` if not specified."},"inOfficePeriod":{"type":"string","nullable":true,"enum":["per_week","per_month","per_year"],"description":"Period denominator for `inOfficeFrequency`, or `null` if not specified."},"inOfficeFrequency":{"type":"number","nullable":true,"description":"How often the candidate must be in-office over `inOfficePeriod`, or `null` if not specified."},"company":{"$ref":"#/components/schemas/JobCompany"},"officeLocations":{"type":"array","items":{"$ref":"#/components/schemas/JobOfficeLocation"},"description":"Physical office locations associated with the job."}},"required":["id","object","title","slug","status","companyId","employmentType","remoteOption","seniority","salaryMin","salaryMax","salaryCurrency","salaryTimeframe","isFeatured","publishedAt","expiresAt","createdAt","updatedAt","externalId","links","description","applicationUrl","remotePermits","remoteWorldwide","remoteTimezones","remoteAllowedTzOffsets","remoteWorkPermitCountryCodes","remoteWorkPermitSubdivisionCodes","remoteSponsorship","educationRequirements","experienceMonths","experienceInPlaceOfEducation","inOfficePeriod","inOfficeFrequency","company","officeLocations"]},"PublicSearchJobsBody":{"type":"object","properties":{"query":{"type":"string","maxLength":200,"description":"Free-text search query matched against job title and description. Up to 200 characters."},"filters":{"type":"object","properties":{"companyId":{"type":"array","items":{"type":"string"},"maxItems":10,"description":"Only return jobs at any of the given company IDs. Up to 10 values."},"remoteOption":{"type":"array","items":{"type":"string","enum":["on_site","hybrid","remote"]},"maxItems":10,"description":"Only return jobs with any of the given remote-work options. Up to 10 values."},"employmentType":{"type":"array","items":{"type":"string","enum":["full_time","part_time","contract","internship","temporary","volunteer","other"]},"maxItems":10,"description":"Only return jobs with any of the given employment types. Up to 10 values."},"seniority":{"type":"array","items":{"type":"string","enum":["entry_level","associate","mid_level","senior","lead","principal","director","executive"]},"maxItems":10,"description":"Only return jobs at any of the given seniority levels. Up to 10 values."},"publishedAt":{"type":"object","properties":{"gte":{"type":"string","format":"date-time","description":"Only return jobs published at or after this ISO 8601 datetime."},"lte":{"type":"string","format":"date-time","description":"Only return jobs published at or before this ISO 8601 datetime."}},"additionalProperties":false,"description":"Range filter on the job's `publishedAt` timestamp."}},"additionalProperties":false,"description":"Optional faceted filters to narrow search results. Multi-value filters match jobs in any of the supplied values; range filters accept `gte` and `lte` bounds."},"cursor":{"type":"string","minLength":1,"description":"An opaque pagination cursor returned in the `nextCursor` field of a previous response. Pass it back to fetch the next page of results."},"limit":{"type":"integer","minimum":1,"maximum":100,"description":"A limit on the number of objects to be returned. Limit can range between 1 and 100.","example":20}},"additionalProperties":false},"SavedJob":{"type":"object","properties":{"id":{"type":"string","description":"Saved-job row ID."},"object":{"type":"string","enum":["saved_job"]},"jobId":{"type":"string","description":"The saved job — also the DELETE path key."},"savedAt":{"type":"string","format":"date-time"},"job":{"$ref":"#/components/schemas/PublicJob"}},"required":["id","object","jobId","savedAt","job"]},"SaveJobBody":{"type":"object","properties":{"jobId":{"type":"string","minLength":1}},"required":["jobId"],"additionalProperties":false},"CompaniesBatchRequest":{"type":"object","properties":{"operations":{"type":"array","items":{"oneOf":[{"type":"object","properties":{"id":{"type":"string","minLength":1},"method":{"type":"string","enum":["POST"]},"body":{"nullable":true},"action":{"type":"string"},"resourceId":{"type":"string"}},"required":["id","method"]},{"type":"object","properties":{"id":{"type":"string","minLength":1},"method":{"type":"string","enum":["PATCH"]},"body":{"nullable":true},"resourceId":{"type":"string","minLength":1}},"required":["id","method","resourceId"]},{"type":"object","properties":{"id":{"type":"string","minLength":1},"method":{"type":"string","enum":["DELETE"]},"resourceId":{"type":"string","minLength":1}},"required":["id","method","resourceId"]}]},"minItems":1,"maxItems":100,"description":"Array of sub-operations to execute. Each entry runs independently and reports its result on the corresponding entry of the response `data` array. Sub-operation `id` values must be unique within the batch. Up to 100 entries."}},"required":["operations"],"additionalProperties":false},"DuplicateJobBody":{"type":"object","properties":{},"additionalProperties":false},"PublishJobBody":{"type":"object","properties":{"expiresAt":{"type":"string","nullable":true,"format":"date-time","description":"New expiry as an ISO 8601 datetime. Pass `null` to clear the expiry. When omitted, the server preserves the existing expiry **if it is still in the future**; a stored expiry in the past (e.g. left over from a prior `expire` call) is cleared automatically so a republished job does not land in an immediately-invisible state."}},"additionalProperties":false},"BatchRequestBody":{"type":"object","properties":{"operations":{"type":"array","items":{"oneOf":[{"type":"object","properties":{"id":{"type":"string","minLength":1},"method":{"type":"string","enum":["POST"]},"body":{"nullable":true},"action":{"type":"string"},"resourceId":{"type":"string"}},"required":["id","method"]},{"type":"object","properties":{"id":{"type":"string","minLength":1},"method":{"type":"string","enum":["PATCH"]},"body":{"nullable":true},"resourceId":{"type":"string","minLength":1}},"required":["id","method","resourceId"]},{"type":"object","properties":{"id":{"type":"string","minLength":1},"method":{"type":"string","enum":["DELETE"]},"resourceId":{"type":"string","minLength":1}},"required":["id","method","resourceId"]}]},"minItems":1,"maxItems":100,"description":"Array of sub-operations to execute. Each entry runs independently and reports its result on the corresponding entry of the response `data` array. Sub-operation `id` values must be unique within the batch. Up to 100 entries."}},"required":["operations"],"additionalProperties":false},"UpdateJobBody":{"type":"object","properties":{"companyId":{"type":"string","description":"Identifier of the company the job belongs to."},"description":{"type":"string","maxLength":25000,"description":"Long-form description of the role. Up to 25,000 characters."},"slug":{"type":"string","description":"URL-friendly slug for the job. Auto-generated from `title` when omitted."},"employmentType":{"type":"string","enum":["full_time","part_time","contract","internship","temporary","volunteer","other"],"description":"Employment type of the role."},"remoteOption":{"type":"string","enum":["on_site","hybrid","remote"],"description":"Whether the role is on-site, hybrid, or fully remote."},"remotePermits":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["worldwide","world_region","continent","region","subregion","subdivision","country","custom"]},"value":{"type":"string","minLength":1}},"required":["type","value"],"additionalProperties":false},"description":"Where remote candidates must hold work authorization. Each entry is the smallest relevant scope: `worldwide`, a `world_region` (EMEA / LATAM / NA / APAC), a `continent`, a `region`, a `subregion`, a `custom` group (e.g. `EU`), a `country` (ISO 3166-1 alpha-2), or a `subdivision` (ISO 3166-2). Subdivisions auto-imply their parent country in the derived `remoteWorkPermitCountryCodes` output. Worldwide is mutually exclusive with all other entries. The canonical `{type, value}` set is published at `GET /v1/taxonomies/remote-permits`. Pass `[]` to clear an existing constraint."},"remoteTimezones":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["all","world_region","continent","region","subregion","country","timezone"]},"value":{"type":"string","minLength":1},"plusMinus":{"type":"number","minimum":0,"maximum":12}},"required":["type","value"],"additionalProperties":false},"description":"Where remote candidates must be timezone-compatible. Each entry mirrors the `remotePermits` shape (`world_region`, `continent`, `region`, `subregion`, `country`) plus `timezone` (specific IANA name with optional `plusMinus` ±N hours expansion) and `all` (every timezone — equivalent of `worldwide` for permits). The canonical `{type, value}` set is published at `GET /v1/taxonomies/remote-timezones`. **When omitted on POST**, the server auto-derives this from `remotePermits` (or `[{all,all}]` if neither was provided). **PATCH never auto-re-derives** — once set, only an explicit replacement updates the stored value, even when `remotePermits` changes. Pass `[]` to clear an existing constraint."},"remoteSponsorship":{"type":"string","enum":["yes","no","unknown"],"description":"Whether the employer sponsors visas for remote candidates. One of `yes`, `no`, or `unknown`."},"seniority":{"type":"string","enum":["entry_level","associate","mid_level","senior","lead","principal","director","executive"],"description":"Seniority level of the role."},"applicationUrl":{"type":"string","minLength":1,"maxLength":2048,"description":"Where candidates apply. Accepts an HTTPS URL, a `mailto:` URI, or a bare email address (which is normalized to `mailto:` form)."},"salaryMin":{"type":"number","minimum":0,"description":"Minimum salary, in `salaryCurrency` units."},"salaryMax":{"type":"number","minimum":0,"description":"Maximum salary, in `salaryCurrency` units."},"salaryCurrency":{"type":"string","description":"Three-letter ISO 4217 currency code for `salaryMin` and `salaryMax`."},"salaryTimeframe":{"type":"string","enum":["per_year","per_month","per_week","per_day","per_hour"],"description":"Period the `salaryMin` and `salaryMax` figures are quoted against."},"isFeatured":{"type":"boolean","description":"Whether the job appears in featured slots on the public board."},"expiresAt":{"anyOf":[{"type":"integer","minimum":0},{"nullable":true},{"nullable":true}],"description":"Job expiry as a Unix epoch in milliseconds. On create, omitted or `null` defaults to 30 days from creation. On PATCH, pass `null` to clear an existing expiry. Past timestamps remove the job from the public board."},"publishedAt":{"type":"integer","minimum":0,"description":"Time at which the job was first published, as a Unix epoch in milliseconds. When omitted on create with `status: \"published\"`, the server stamps the current time. Useful for bulk-importing historical jobs while preserving original publication dates. PATCH may overwrite an existing value but cannot clear it."},"educationRequirements":{"type":"array","items":{"type":"string","enum":["high_school","associate_degree","bachelor_degree","professional_certificate","postgraduate_degree","no_requirements"]},"description":"Required education credentials. Each value is one of `high_school`, `associate_degree`, `bachelor_degree`, `professional_certificate`, `postgraduate_degree`, or `no_requirements`."},"experienceMonths":{"type":"integer","minimum":0,"description":"Minimum required experience, expressed in months."},"experienceInPlaceOfEducation":{"type":"boolean","description":"If `true`, equivalent experience may substitute for the listed education requirements."},"inOfficePeriod":{"type":"string","enum":["per_week","per_month","per_year"],"description":"Period denominator for `inOfficeFrequency`."},"inOfficeFrequency":{"type":"number","minimum":0,"description":"How often the candidate must be in-office over `inOfficePeriod`."},"officeLocations":{"type":"array","items":{"$ref":"#/components/schemas/JobOfficeLocationInput"},"description":"Physical office locations associated with the job. Each entry is forward-geocoded server-side; a country mismatch returns `400 jobs_unresolvable_location`."},"externalId":{"type":"string","minLength":1,"maxLength":255,"description":"An external identifier for the job from your own system, such as an ATS requisition ID. Use this value to look up the job later via `GET /v1/jobs?externalId=...` for deduplication. Scoped per-account: two different accounts may reuse the same `externalId` without collision. Up to 255 characters."},"customFieldValues":{"type":"object","additionalProperties":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}},{"type":"boolean"},{"type":"number"},{"nullable":true},{"nullable":true}]},"description":"Board-defined custom-field values, keyed by the field `key` (definitions, including type and option keys, are published at `GET /v1/settings/job-form`). Writes are **additive**: on `PATCH` a key you send is set/overwritten and a key you omit is preserved (unsent keys are never cleared); on `POST` this initializes the bag. Send a key with an intentional-empty value — `null`, `\"\"`, or `[]` — to **clear** it (`\"\"`/`null` clear any type; `[]` clears a `multi_select`); `false` and `0` are kept as real values. Values must match the field type and `single_select`/`multi_select` must use defined option **keys** (not labels); a wrong-typed value is rejected (`custom_field_wrong_type`), never silently cleared. Unknown keys are ignored. The stored bag never contains `null`/empty values."},"title":{"type":"string","minLength":1,"maxLength":200,"description":"The job title."}},"additionalProperties":false},"SearchJobsBody":{"type":"object","properties":{"query":{"type":"string","maxLength":200,"description":"Free-text search query matched against job title and description. Up to 200 characters."},"filters":{"type":"object","properties":{"status":{"type":"array","items":{"type":"string","enum":["draft","published","expired","archived"]},"maxItems":10,"description":"Only return jobs in any of the given statuses. Up to 10 values."},"companyId":{"type":"array","items":{"type":"string"},"maxItems":10,"description":"Only return jobs at any of the given company IDs. Up to 10 values."},"remoteOption":{"type":"array","items":{"type":"string","enum":["on_site","hybrid","remote"]},"maxItems":10,"description":"Only return jobs with any of the given remote-work options. Up to 10 values."},"employmentType":{"type":"array","items":{"type":"string","enum":["full_time","part_time","contract","internship","temporary","volunteer","other"]},"maxItems":10,"description":"Only return jobs with any of the given employment types. Up to 10 values."},"seniority":{"type":"array","items":{"type":"string","enum":["entry_level","associate","mid_level","senior","lead","principal","director","executive"]},"maxItems":10,"description":"Only return jobs at any of the given seniority levels. Up to 10 values."},"publishedAt":{"type":"object","properties":{"gte":{"type":"string","format":"date-time","description":"Only return jobs published at or after this ISO 8601 datetime."},"lte":{"type":"string","format":"date-time","description":"Only return jobs published at or before this ISO 8601 datetime."}},"additionalProperties":false,"description":"Range filter on the job's `publishedAt` timestamp."}},"additionalProperties":false,"description":"Optional faceted filters to narrow search results. Multi-value filters match jobs in any of the supplied values; range filters accept `gte` and `lte` bounds."},"cursor":{"type":"string","minLength":1,"description":"An opaque pagination cursor returned in the `nextCursor` field of a previous response. Pass it back to fetch the next page of results."},"limit":{"type":"integer","minimum":1,"maximum":100,"description":"A limit on the number of objects to be returned. Limit can range between 1 and 100.","example":20}},"additionalProperties":false},"MediaUpload":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier for the object. Use this value as the `{id}` path parameter for `GET /v1/media/{id}`."},"object":{"type":"string","enum":["media_upload"],"description":"String representing the object's type. Objects of the same type share the same value."},"url":{"type":"string","nullable":true,"description":"Signed URL where the file can be downloaded. Always set on the upload response; may be `null` on retrieval if the underlying blob has been deleted."},"urlExpiresAt":{"type":"string","description":"Time at which the signed `url` expires. Approximately 15 minutes after the response is issued. ISO 8601 datetime."},"mimeType":{"type":"string","description":"MIME type of the uploaded file."},"sizeBytes":{"type":"number","description":"Size of the uploaded file, in bytes."},"purpose":{"type":"string","enum":["board_logo","board_hero","account_avatar","company_logo","blog_image"],"description":"Resource type the file was uploaded for."}},"required":["id","object","url","urlExpiresAt","mimeType","sizeBytes","purpose"],"x-internal":true},"MediaGet":{"allOf":[{"$ref":"#/components/schemas/MediaUpload"},{"type":"object","properties":{"uploadedAt":{"type":"string","description":"Time at which the file was uploaded. ISO 8601 datetime."},"referencedBy":{"type":"array","items":{"type":"object","properties":{"resourceType":{"type":"string","description":"Type of the resource referencing this file."},"resourceId":{"type":"string","description":"Identifier of the resource referencing this file."}},"required":["resourceType","resourceId"]},"description":"Resources currently referencing this file. Currently always `[]` — the resolver is not yet implemented."}},"required":["uploadedAt","referencedBy"]}],"x-internal":true},"DynamicClientRegistrationResponse":{"type":"object","properties":{"client_id":{"type":"string","description":"Public OAuth client identifier."},"client_id_issued_at":{"type":"number","description":"Time at which the client was registered. Unix epoch in seconds."},"client_secret":{"type":"string","description":"Plaintext OAuth client secret. Returned exactly once for confidential clients; omitted for public clients."},"client_secret_expires_at":{"type":"number","description":"Time at which the client secret expires. Unix epoch in seconds, or `0` if the secret does not expire. Omitted for public clients."},"redirect_uris":{"type":"array","items":{"type":"string"},"description":"Array of redirect URIs registered for the client."},"client_name":{"type":"string","description":"Human-readable client name shown to users on the consent screen."},"grant_types":{"type":"array","items":{"type":"string"},"description":"OAuth grant types the client is permitted to use."},"response_types":{"type":"array","items":{"type":"string"},"description":"OAuth response types the client is permitted to use."},"token_endpoint_auth_method":{"type":"string","description":"Authentication method the client uses at the token endpoint."},"scope":{"type":"string","description":"Space-separated list of scopes the client may request."}},"required":["client_id","client_id_issued_at","redirect_uris","client_name","grant_types","response_types","token_endpoint_auth_method","scope"],"x-internal":true},"DynamicClientRegistrationRequest":{"type":"object","properties":{"redirect_uris":{"type":"array","items":{"type":"string","minLength":1},"minItems":1,"description":"Array of redirect URIs the client may use. Must be HTTPS, except for `http://localhost` URIs in development. At least one entry is required."},"client_name":{"type":"string","maxLength":256,"description":"Human-readable client name shown to users on the consent screen. Recommended for MCP clients; defaults to \"MCP client\" when omitted."},"scope":{"type":"string","description":"Space-separated list of scopes the client may request. When omitted, the client is registered for all scopes."},"token_endpoint_auth_method":{"type":"string","enum":["client_secret_basic","none"],"description":"OAuth token endpoint authentication method. Use `none` for public PKCE clients that cannot keep a client secret."}},"required":["redirect_uris"],"x-internal":true},"Usage":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier for the object. Equal to the account ID — usage is a per-account singleton."},"object":{"type":"string","enum":["usage"],"description":"String representing the object's type. Objects of the same type share the same value."},"activeJobs":{"type":"number","description":"Number of jobs currently in `published` status."},"activeJobsLimit":{"type":"number","description":"Plan-enforced cap on the number of published jobs."},"available":{"type":"number","description":"Number of additional jobs the account may publish before hitting the limit."},"plan":{"type":"string","description":"Slug of the account's current plan."}},"required":["id","object","activeJobs","activeJobsLimit","available","plan"]},"PublicBoardContext":{"type":"object","properties":{"object":{"type":"string","enum":["public_board"]},"id":{"type":"string","description":"Immutable board identifier (`boards_…`)."},"slug":{"type":"string","description":"Mutable canonical board slug."},"name":{"type":"string"},"language":{"type":"string","description":"Board language (ISO code); defaults to \"en\"."},"logoUrl":{"type":"string","nullable":true},"primaryDomain":{"type":"string","nullable":true,"description":"Active custom-domain hostname, or null."},"features":{"type":"object","properties":{"jobAlerts":{"type":"boolean"},"candidates":{"type":"boolean"},"employers":{"type":"boolean"},"blog":{"type":"boolean"},"talentDirectory":{"type":"boolean"},"registrationWall":{"type":"boolean"},"passwordProtected":{"type":"boolean"},"publicJobSubmission":{"type":"boolean"},"candidatePaywall":{"type":"boolean"}},"required":["jobAlerts","candidates","employers","blog","talentDirectory","registrationWall","passwordProtected","publicJobSubmission","candidatePaywall"]},"analytics":{"type":"object","properties":{"ga4MeasurementId":{"type":"string","nullable":true},"gtmId":{"type":"string","nullable":true},"metaPixelId":{"type":"string","nullable":true},"linkedInPartnerId":{"type":"string","nullable":true},"cookieConsentRequired":{"type":"boolean"}},"required":["ga4MeasurementId","gtmId","metaPixelId","linkedInPartnerId","cookieConsentRequired"]},"theme":{"type":"object","nullable":true,"properties":{"mode":{"type":"string"},"schemeId":{"type":"string"},"typography":{"type":"object","properties":{"fontSans":{"type":"string"},"fontHeading":{"type":"string","nullable":true}},"required":["fontSans"]},"colors":{"type":"object","properties":{"light":{"type":"object","additionalProperties":{"nullable":true}},"dark":{"type":"object","additionalProperties":{"nullable":true}}},"required":["light","dark"]}},"required":["mode","schemeId","typography","colors"]}},"required":["object","id","slug","name","language","logoUrl","primaryDomain","features","analytics","theme"]},"BoardUser":{"type":"object","properties":{"id":{"type":"string","description":"Board user ID."},"object":{"type":"string","enum":["board_user"]},"role":{"type":"string","enum":["candidate","employer"]},"email":{"type":"string"},"displayName":{"type":"string","nullable":true},"emailVerified":{"type":"boolean"}},"required":["id","object","role","email","displayName","emailVerified"]},"BoardAuthSession":{"type":"object","properties":{"object":{"type":"string","enum":["board_auth_session"]},"accessToken":{"type":"string","description":"Short-lived JWT (1 hour). Send as `Authorization: Bearer`."},"refreshToken":{"type":"string","description":"Opaque single-use refresh token (30 days). Each refresh returns a replacement; a reused token is rejected."},"expiresAt":{"type":"number","description":"Access-token expiry, epoch milliseconds."},"boardUser":{"$ref":"#/components/schemas/BoardUser"}},"required":["object","accessToken","refreshToken","expiresAt","boardUser"]},"BoardAuthRegisterBody":{"type":"object","properties":{"role":{"type":"string","enum":["candidate","employer"],"description":"Which role profile to create on the board."},"method":{"type":"string","enum":["emailpass"],"description":"Registration method. Only `emailpass` is supported."},"email":{"type":"string","format":"email"},"password":{"type":"string","minLength":8,"description":"Minimum 8 characters."},"displayName":{"type":"string","minLength":1,"maxLength":100}},"required":["role","method","email","password","displayName"]},"BoardAuthLoginBody":{"type":"object","properties":{"email":{"type":"string","format":"email"},"password":{"type":"string"}},"required":["email","password"]},"BoardAuthRefreshBody":{"type":"object","properties":{"refreshToken":{"type":"string"}},"required":["refreshToken"]},"BoardAuthLogoutBody":{"type":"object","properties":{"refreshToken":{"type":"string"}},"required":["refreshToken"]},"BoardAuthForgotPasswordBody":{"type":"object","properties":{"email":{"type":"string","format":"email"}},"required":["email"]},"BoardAuthResetPasswordBody":{"type":"object","properties":{"token":{"type":"string"},"password":{"type":"string","minLength":8,"description":"Minimum 8 characters."}},"required":["token","password"]},"BoardAuthVerifyEmailBody":{"type":"object","properties":{"token":{"type":"string"}},"required":["token"]},"TokenResponse":{"type":"object","properties":{"access_token":{"type":"string","description":"Newly issued access token. Use as a `Bearer` token in the `Authorization` header."},"token_type":{"type":"string","enum":["Bearer"],"description":"Always `Bearer`."},"expires_in":{"type":"number","description":"Lifetime of the access token, in seconds."},"refresh_token":{"type":"string","description":"Newly issued refresh token. Single-use; reusing it revokes the entire token chain."},"scope":{"type":"string","description":"Space-separated list of scopes granted on the issued token."}},"required":["access_token","token_type","expires_in","refresh_token","scope"],"x-internal":true},"TokenRequest":{"type":"object","properties":{"grant_type":{"type":"string","enum":["authorization_code","refresh_token"],"description":"OAuth grant type. Either `authorization_code` to exchange an authorization code for a new token pair, or `refresh_token` to exchange a refresh token for a new token pair."},"code":{"type":"string","description":"Authorization code returned from the authorization endpoint. Required when `grant_type` is `authorization_code`."},"redirect_uri":{"type":"string","description":"Redirect URI used in the original authorization request. Must match exactly. Required when `grant_type` is `authorization_code`."},"code_verifier":{"type":"string","description":"PKCE code verifier whose `S256` hash matches the original `code_challenge`. Required when `grant_type` is `authorization_code`."},"refresh_token":{"type":"string","description":"Refresh token previously issued. Required when `grant_type` is `refresh_token`. Refresh tokens are single-use; reusing one revokes the entire token chain."},"client_id":{"type":"string","description":"OAuth client identifier. May be supplied here or via HTTP Basic authentication."},"client_secret":{"type":"string","description":"OAuth client secret. May be supplied here or via HTTP Basic authentication."},"scope":{"type":"string","description":"Optional scope narrowing. Currently ignored — issued tokens always carry the full scope registered for the client."}},"required":["grant_type"],"x-internal":true},"RevokeRequest":{"type":"object","properties":{"token":{"type":"string","minLength":1,"description":"The access or refresh token to revoke."},"token_type_hint":{"type":"string","enum":["access_token","refresh_token"],"description":"Hint indicating whether `token` is an `access_token` or `refresh_token`. Optional."},"client_id":{"type":"string","description":"OAuth client identifier. May be supplied here or via HTTP Basic authentication."},"client_secret":{"type":"string","description":"OAuth client secret. May be supplied here or via HTTP Basic authentication."}},"required":["token"],"x-internal":true},"OperationProgress":{"type":"object","nullable":true,"properties":{"percent":{"type":"number","description":"Progress as a percentage between 0 and 100, when the operation reports a percentage."},"message":{"type":"string","description":"Human-readable progress message, when available."},"processed":{"type":"number","description":"Number of items processed so far, when applicable."},"total":{"type":"number","description":"Total number of items to process, when known."}},"description":"Snapshot of the operation's progress, or `null` if the operation does not report progress.","x-internal":true},"OperationErrorEnvelope":{"type":"object","nullable":true,"properties":{"code":{"type":"string","description":"Machine-readable error code."},"message":{"type":"string","description":"Human-readable error message."},"details":{"nullable":true,"description":"Additional structured details about the error, when present."},"requestId":{"type":"string","description":"Identifier of the request that produced the error. Use this to correlate with platform logs."}},"required":["code","message"],"description":"Error envelope, populated once the operation reaches `failed`. `null` otherwise.","x-internal":true},"OperationResource":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier for the object."},"object":{"type":"string","enum":["operation"],"description":"String representing the object's type. Objects of the same type share the same value."},"kind":{"type":"string","description":"Operation kind, identifying which workflow the operation runs."},"state":{"type":"string","enum":["pending","running","succeeded","failed","cancelled"],"description":"Current state of the operation."},"accountId":{"type":"string","description":"Identifier of the account the operation belongs to."},"progress":{"$ref":"#/components/schemas/OperationProgress"},"result":{"nullable":true,"description":"Operation result, populated once the operation reaches `succeeded`. Shape varies by `kind`."},"error":{"$ref":"#/components/schemas/OperationErrorEnvelope"},"createdAt":{"type":"string","description":"Time at which the operation was created. ISO 8601 datetime."},"updatedAt":{"type":"string","description":"Time at which the operation was last updated. ISO 8601 datetime."},"completedAt":{"type":"string","nullable":true,"description":"Time at which the operation reached a terminal state, or `null` if it has not yet completed. ISO 8601 datetime."},"cancelRequested":{"type":"boolean","description":"Whether cancellation of the operation has been requested."},"resourceType":{"type":"string","nullable":true,"description":"Type of resource the operation acts on, or `null` for resource-less operations."},"resourceId":{"type":"string","nullable":true,"description":"Identifier of the resource the operation acts on, or `null` for resource-less operations."},"parentOperationId":{"type":"string","nullable":true,"description":"Identifier of the parent operation when this operation is a child of a fan-out, or `null` otherwise."}},"required":["id","object","kind","state","accountId","progress","error","createdAt","updatedAt","completedAt","cancelRequested","resourceType","resourceId","parentOperationId"],"x-internal":true},"PatchSettingsBody":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200,"description":"Display name of the board."},"slug":{"type":"string","minLength":1,"maxLength":120,"description":"URL slug of the board. Lowercase alphanumeric and hyphens; reserved values (e.g. `admin`, `api`) are rejected."},"primaryDomainId":{"type":"string","description":"Identifier of the custom domain to use as the primary public URL."},"logoStorageId":{"type":"string","description":"Media storage ID returned by `POST /v1/media/upload` with `purpose=board_logo`."},"heroStorageId":{"type":"string","description":"Media storage ID returned by `POST /v1/media/upload` with `purpose=board_hero`."},"passwordProtectionEnabled":{"type":"boolean","description":"Whether the board sits behind a password gate. Use `POST /v1/settings/password-protection` to set the actual password before enabling."},"jobAlertsEnabled":{"type":"boolean","description":"Whether candidates can subscribe to email alerts for new jobs."},"candidatesEnabled":{"type":"boolean","description":"Whether candidate profiles are enabled on the board."},"employersEnabled":{"type":"boolean","description":"Whether employer self-serve flows are enabled."},"blogEnabled":{"type":"boolean","description":"Whether the public blog is exposed."},"impressumEnabled":{"type":"boolean","description":"Whether the legal Impressum page is exposed (required in some jurisdictions)."},"requireApprovalFreeJobs":{"type":"boolean","description":"Whether jobs posted by employers from the free tier require admin approval before they appear on the public board."},"requireApprovalAggregatedJobs":{"type":"boolean","description":"Whether aggregated jobs (e.g. those scraped or imported from upstream sources) require admin approval before they appear on the public board."},"registrationWallEnabled":{"type":"boolean","description":"Whether visitors must sign in to view jobs."},"talentDirectoryEnabled":{"type":"boolean","description":"Whether the public talent directory is enabled."},"showCavunoBranding":{"type":"boolean","description":"Whether the Cavuno-branded footer is displayed on the public board. Plan-gated; lower-tier plans cannot disable this."},"defaultJobDurationDays":{"type":"integer","minimum":0,"exclusiveMinimum":true,"description":"Default expiry, in days, applied to newly published jobs when no `expiresAt` is supplied."},"contactEmail":{"anyOf":[{"type":"string","maxLength":320,"format":"email"},{"type":"string","enum":[""]},{"nullable":true},{"nullable":true}],"description":"Public contact email shown on the board. Pass `null` to clear."},"companyLegalName":{"anyOf":[{"type":"string","maxLength":200},{"type":"string","enum":[""]},{"nullable":true},{"nullable":true}],"description":"Legal name of the entity operating the board. Pass `null` to clear."},"companyAddress":{"anyOf":[{"type":"string","maxLength":500},{"type":"string","enum":[""]},{"nullable":true},{"nullable":true}],"description":"Postal address of the entity operating the board. Pass `null` to clear."},"companyWebsiteUrl":{"anyOf":[{"type":"string","maxLength":500},{"type":"string","enum":[""]},{"nullable":true},{"nullable":true}],"description":"Public company website URL. Pass `null` to clear."},"companyXHandle":{"anyOf":[{"type":"string","maxLength":500},{"type":"string","enum":[""]},{"nullable":true},{"nullable":true}],"description":"Company X (Twitter) handle or profile URL. Pass `null` to clear."},"companyFacebookUrl":{"anyOf":[{"type":"string","maxLength":500},{"type":"string","enum":[""]},{"nullable":true},{"nullable":true}],"description":"Company Facebook page URL. Pass `null` to clear."},"companyLinkedinUrl":{"anyOf":[{"type":"string","maxLength":500},{"type":"string","enum":[""]},{"nullable":true},{"nullable":true}],"description":"Company LinkedIn page URL. Pass `null` to clear."},"talentNavLabel":{"type":"string","minLength":1,"maxLength":50,"description":"Label shown for the talent directory in the public navigation. 1–50 characters."},"passwordProtectionMessage":{"anyOf":[{"type":"string","maxLength":500},{"type":"string","enum":[""]},{"nullable":true},{"nullable":true}],"description":"Custom copy displayed on the password gate. Up to 500 characters. Pass `null` to clear."},"config":{"type":"object","additionalProperties":{"nullable":true},"description":"Free-form configuration object merged into the existing config. Reserved keys (e.g. `passwordProtectionEnabled`, `adsenseClientId`, `jobAccessPaywallEnabled`) cannot be set here — use the dedicated endpoints instead."}},"additionalProperties":false,"x-internal":true},"SettingsAdsenseSlot":{"type":"object","properties":{"enabled":{"type":"boolean","description":"Whether this placement is rendered."},"slotId":{"anyOf":[{"type":"string","pattern":"^[0-9]{10}$"},{"type":"string","enum":[""]},{"nullable":true},{"nullable":true}],"description":"Ten-digit AdSense slot ID. Pass an empty string or `null` to clear."},"layout":{"anyOf":[{"type":"string","enum":["auto","in-article","in-feed","fluid"]},{"type":"string","enum":[""]},{"nullable":true},{"nullable":true}],"description":"AdSense layout type for the placement."},"format":{"anyOf":[{"type":"string","enum":["auto","horizontal","vertical","rectangle","responsive"]},{"type":"string","enum":[""]},{"nullable":true},{"nullable":true}],"description":"AdSense ad format."},"style":{"anyOf":[{"type":"string","enum":["default","light","dark","contrast"]},{"type":"string","enum":[""]},{"nullable":true},{"nullable":true}],"description":"Visual style override for the placement."},"frequency":{"anyOf":[{"type":"integer","minimum":1,"maximum":50},{"nullable":true},{"nullable":true}],"description":"How often the placement is shown (e.g. once every N items)."}},"required":["enabled","slotId"],"x-internal":true},"SettingsAdsenseBody":{"type":"object","properties":{"adsenseEnabled":{"type":"boolean","description":"Whether AdSense placements are rendered on the public board."},"adsenseClientId":{"anyOf":[{"type":"string","maxLength":25,"pattern":"^ca-pub-\\d{16}$"},{"type":"string","enum":[""]},{"nullable":true},{"nullable":true}],"description":"AdSense publisher ID in `ca-pub-XXXXXXXXXXXXXXXX` format. Pass an empty string or `null` to clear."},"adsenseSlots":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/SettingsAdsenseSlot"},"description":"Map of placement key to slot configuration. Placement keys are lowercase alphanumeric with `-`, `_`, `.`, or `:`."},"adsTxt":{"anyOf":[{"type":"string","maxLength":2000},{"type":"string","enum":[""]},{"nullable":true},{"nullable":true}],"description":"Contents to serve from `/ads.txt`. Up to 2,000 characters. Pass an empty string or `null` to clear."}},"additionalProperties":false,"x-internal":true},"SettingsPaywallBody":{"type":"object","properties":{"jobAccessPaywallEnabled":{"type":"boolean","description":"Whether the job-access paywall is enforced on the public board."},"jobAccessPreviewCount":{"type":"integer","minimum":1,"maximum":500,"description":"Number of jobs visible before the paywall locks the rest. Range 1–500."},"jobAccessLockHeading":{"type":"string","maxLength":120,"description":"Heading shown on the paywall. Up to 120 characters."},"jobAccessLockDescription":{"type":"string","maxLength":600,"description":"Body copy shown on the paywall. Up to 600 characters."},"jobAccessButtonText":{"type":"string","maxLength":60,"description":"Call-to-action label shown on the paywall. Up to 60 characters."},"jobAccessDisclaimerText":{"type":"string","maxLength":200,"description":"Fine-print disclaimer shown beneath the call-to-action. Up to 200 characters."},"jobAccessPerMonthLabel":{"type":"string","maxLength":60,"description":"Label appended to the monthly price (e.g. `/month`). Up to 60 characters."},"jobAccessSavingsTemplate":{"type":"string","maxLength":120,"description":"Template string used to render the savings line for annual plans. Up to 120 characters."},"jobAccessCurrency":{"type":"string","minLength":3,"maxLength":3,"description":"Three-letter ISO 4217 currency code for paywall pricing."},"jobAccessStripeProductId":{"anyOf":[{"type":"string"},{"nullable":true},{"nullable":true}],"description":"Stripe product ID associated with the paywall plan. Pass `null` to clear."},"jobAccessStripePortalConfigId":{"anyOf":[{"type":"string"},{"nullable":true},{"nullable":true}],"description":"Stripe billing-portal configuration ID used for self-serve plan management. Pass `null` to clear."}},"required":["jobAccessPaywallEnabled"],"additionalProperties":false,"x-internal":true},"SettingsPasswordProtectionBody":{"type":"object","properties":{"password":{"type":"string","minLength":8,"description":"Plaintext password used to gate the public board. Must be at least 8 characters. Stored hashed and encrypted server-side."}},"required":["password"],"additionalProperties":false,"x-internal":true},"CustomFieldOption":{"type":"object","properties":{"key":{"type":"string","description":"Stable option key — send this in a write, not the label."},"label":{"type":"string","description":"Display label (authoring default; localized per board in the template)."}},"required":["key","label"],"x-internal":true},"CustomFieldDefinition":{"type":"object","properties":{"key":{"type":"string","description":"Immutable per-board slug. Use this as the key in `customFieldValues` when writing values via POST/PATCH /v1/jobs."},"label":{"type":"string","description":"Authoring-default label; the localized public string lives in the board template."},"type":{"type":"string","enum":["short_text","long_text","single_select","multi_select","boolean","number"],"description":"Field type, which dictates the accepted value: `short_text`/`long_text` → string; `single_select` → one option key (string); `multi_select` → array of option keys; `boolean` → boolean; `number` → number."},"options":{"type":"array","items":{"$ref":"#/components/schemas/CustomFieldOption"},"description":"Present only for `single_select`/`multi_select`. A written value must be one (or, for multi, several) of these option `key`s — never a label."},"required":{"type":"boolean","description":"When true, the value cannot be cleared or left empty on a write (rejected with `custom_field_required`)."},"min":{"type":"number","description":"Inclusive minimum for `number` fields, when set."},"max":{"type":"number","description":"Inclusive maximum for `number` fields, when set."}},"required":["key","label","type","required"],"x-internal":true},"JobFormConfig":{"type":"object","properties":{"sponsorship":{"type":"object","properties":{"visible":{"type":"boolean"}},"required":["visible"]},"salary":{"type":"object","properties":{"visible":{"type":"boolean"},"required":{"type":"boolean"},"minBound":{"type":"number"},"maxBound":{"type":"number"},"allowedCurrencies":{"type":"array","items":{"type":"string"}}},"required":["visible"]},"seniority":{"type":"object","properties":{"visible":{"type":"boolean"},"required":{"type":"boolean"},"allowedOptions":{"type":"array","items":{"type":"string"}}},"required":["visible"]},"workArrangement":{"type":"object","properties":{"allowedOptions":{"type":"array","items":{"type":"string"}}},"required":["allowedOptions"]},"employmentType":{"type":"object","properties":{"allowedOptions":{"type":"array","items":{"type":"string"}}},"required":["allowedOptions"]},"location":{"type":"object","properties":{"visible":{"type":"boolean"},"allowedCountries":{"type":"array","items":{"type":"string"}}},"required":["visible"]},"customFields":{"type":"array","items":{"$ref":"#/components/schemas/CustomFieldDefinition"},"description":"Board-defined custom field definitions, in display order. Read these to learn which `customFieldValues` keys, types, and option keys a job accepts on POST/PATCH /v1/jobs."}},"x-internal":true},"RemotePermitTaxonomyEntry":{"type":"object","properties":{"type":{"type":"string","description":"Discriminator. One of: `worldwide`, `world_region`, `continent`, `region`, `subregion`, `custom`, `country`, `subdivision`."},"value":{"type":"string","description":"Canonical value for the type. ISO 3166-1 alpha-2 for `country`, ISO 3166-2 for `subdivision`, etc."},"label":{"type":"string","description":"Human-readable display label."}},"required":["type","value","label"]},"RemoteTimezoneTaxonomyEntry":{"type":"object","properties":{"type":{"type":"string","description":"Discriminator. One of: `all`, `world_region`, `continent`, `region`, `subregion`, `country`, `timezone`."},"value":{"type":"string","description":"Canonical value for the type. ISO 3166-1 alpha-2 for `country`, IANA name (e.g. `Europe/London`) for `timezone`, etc."},"label":{"type":"string","description":"Human-readable display label."}},"required":["type","value","label"]}},"parameters":{}},"paths":{"/api-keys":{"get":{"summary":"List all API keys","description":"Returns a list of your API keys. The keys are returned sorted by creation date, with the most recently created keys appearing first. Plaintext secrets are never included; each key is identified by its public prefix `cavuno_live_<keyId>_`. Keys can only be created from the dashboard — the API does not expose a creation endpoint.","parameters":[{"schema":{"type":"string","minLength":1,"description":"An opaque pagination cursor returned in the `nextCursor` field of a previous response. Pass it back to fetch the next page of results."},"required":false,"description":"An opaque pagination cursor returned in the `nextCursor` field of a previous response. Pass it back to fetch the next page of results.","name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50,"description":"A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 50."},"required":false,"description":"A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 50.","name":"limit","in":"query"}],"responses":{"200":{"description":"Successful response.","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","enum":["list"]},"url":{"type":"string","example":"/v1/api-keys"},"hasMore":{"type":"boolean"},"nextCursor":{"type":"string","nullable":true},"data":{"type":"array","items":{"$ref":"#/components/schemas/ApiKey"}}},"required":["object","url","hasMore","nextCursor","data"]}}}}},"tags":["API keys"]}},"/audit-logs":{"get":{"summary":"List all audit log entries","description":"Returns a list of audit log entries for your account. The entries are returned sorted by timestamp, with the most recent entries appearing first. Requires the `audit.read` permission.","parameters":[{"schema":{"type":"string","minLength":1,"description":"An opaque pagination cursor returned in the `nextCursor` field of a previous response. Pass it back to fetch the next page of results."},"required":false,"description":"An opaque pagination cursor returned in the `nextCursor` field of a previous response. Pass it back to fetch the next page of results.","name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50,"description":"A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 50."},"required":false,"description":"A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 50.","name":"limit","in":"query"},{"schema":{"type":"string","description":"Only return audit entries on the given resource type (e.g. `job`, `company`)."},"required":false,"description":"Only return audit entries on the given resource type (e.g. `job`, `company`).","name":"resourceType","in":"query"},{"schema":{"type":"string","enum":["api_key","oauth_token","user_session","system"],"description":"Only return audit entries written by the given actor type."},"required":false,"description":"Only return audit entries written by the given actor type.","name":"actorType","in":"query"},{"schema":{"type":"string","description":"Only return audit entries written by the given actor ID."},"required":false,"description":"Only return audit entries written by the given actor ID.","name":"actorId","in":"query"},{"schema":{"type":"string","format":"date-time","description":"Only return audit entries with a `timestamp` at or after this ISO 8601 datetime."},"required":false,"description":"Only return audit entries with a `timestamp` at or after this ISO 8601 datetime.","name":"from","in":"query"},{"schema":{"type":"string","format":"date-time","description":"Only return audit entries with a `timestamp` at or before this ISO 8601 datetime."},"required":false,"description":"Only return audit entries with a `timestamp` at or before this ISO 8601 datetime.","name":"to","in":"query"}],"responses":{"200":{"description":"Successful response.","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","enum":["list"]},"url":{"type":"string","example":"/v1/audit-logs"},"hasMore":{"type":"boolean"},"nextCursor":{"type":"string","nullable":true},"data":{"type":"array","items":{"$ref":"#/components/schemas/AuditLogEntry"}}},"required":["object","url","hasMore","nextCursor","data"]}}}}},"tags":["Audit logs"],"x-internal":true}},"/audit-logs/{id}":{"get":{"summary":"Retrieve an audit log entry","description":"Retrieves the details of an existing audit log entry. You only need to supply the unique ID that was returned with the entry.","parameters":[{"schema":{"type":"string","minLength":1,"description":"Unique identifier for the audit log entry."},"required":true,"description":"Unique identifier for the audit log entry.","name":"id","in":"path"}],"responses":{"200":{"description":"Successful response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuditLogEntry"}}}}},"tags":["Audit logs"],"x-internal":true}},"/blog/authors":{"get":{"summary":"List all blog authors","description":"Returns a list of your blog authors. The authors are returned sorted by update date, with the most recently updated authors appearing first.","parameters":[{"schema":{"type":"string","minLength":1,"description":"An opaque pagination cursor returned in the `nextCursor` field of a previous response. Pass it back to fetch the next page of results."},"required":false,"description":"An opaque pagination cursor returned in the `nextCursor` field of a previous response. Pass it back to fetch the next page of results.","name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"description":"A limit on the number of objects to be returned. Limit can range between 1 and 100."},"required":false,"description":"A limit on the number of objects to be returned. Limit can range between 1 and 100.","example":20,"name":"limit","in":"query"}],"responses":{"200":{"description":"Successful response.","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","enum":["list"]},"url":{"type":"string","example":"/v1/blog/authors"},"hasMore":{"type":"boolean"},"nextCursor":{"type":"string","nullable":true},"data":{"type":"array","items":{"$ref":"#/components/schemas/BlogAuthor"}}},"required":["object","url","hasMore","nextCursor","data"]}}}},"400":{"description":"The request was malformed."}},"tags":["Other"]},"post":{"summary":"Create a blog author","description":"Creates a new blog author. The request is idempotent when an `Idempotency-Key` header is supplied.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAuthorBody"}}}},"responses":{"201":{"description":"Successful response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BlogAuthor"}}}},"400":{"description":"The request was malformed."},"409":{"description":"Another author already uses the same slug."}},"tags":["Other"]}},"/blog/authors/{id}":{"get":{"summary":"Retrieve a blog author","description":"Retrieves the details of an existing blog author. You only need to supply the unique author ID that was returned upon author creation.","parameters":[{"schema":{"type":"string","minLength":1,"description":"The author's opaque object `id`: the `id` field returned by the author endpoints (e.g. `a4u7t9k2d6q0r3s8v1w5x9y2z6b0c4m7`). This is NOT the `slug` or name; passing a slug like `jane-doe` returns 404. To look up an author by name or slug, use `GET /v1/blog/authors` and read the returned `id`."},"required":true,"description":"The author's opaque object `id`: the `id` field returned by the author endpoints (e.g. `a4u7t9k2d6q0r3s8v1w5x9y2z6b0c4m7`). This is NOT the `slug` or name; passing a slug like `jane-doe` returns 404. To look up an author by name or slug, use `GET /v1/blog/authors` and read the returned `id`.","example":"a4u7t9k2d6q0r3s8v1w5x9y2z6b0c4m7","name":"id","in":"path"}],"responses":{"200":{"description":"Successful response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BlogAuthor"}}}},"404":{"description":"Author not found."}},"tags":["Other"]},"patch":{"summary":"Update a blog author","description":"Updates the specified blog author by setting the values of the parameters passed. Any parameters not provided will be left unchanged. The request is idempotent when an `Idempotency-Key` header is supplied.","parameters":[{"schema":{"type":"string","minLength":1,"description":"The author's opaque object `id`: the `id` field returned by the author endpoints (e.g. `a4u7t9k2d6q0r3s8v1w5x9y2z6b0c4m7`). This is NOT the `slug` or name; passing a slug like `jane-doe` returns 404. To look up an author by name or slug, use `GET /v1/blog/authors` and read the returned `id`."},"required":true,"description":"The author's opaque object `id`: the `id` field returned by the author endpoints (e.g. `a4u7t9k2d6q0r3s8v1w5x9y2z6b0c4m7`). This is NOT the `slug` or name; passing a slug like `jane-doe` returns 404. To look up an author by name or slug, use `GET /v1/blog/authors` and read the returned `id`.","example":"a4u7t9k2d6q0r3s8v1w5x9y2z6b0c4m7","name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateAuthorBody"}}}},"responses":{"200":{"description":"Successful response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BlogAuthor"}}}},"400":{"description":"The request was malformed."},"404":{"description":"Author not found."},"409":{"description":"Another author already uses the same slug."}},"tags":["Other"]},"delete":{"summary":"Delete a blog author","description":"Permanently deletes a blog author. Posts referencing this author keep the stale ID in their `authorIds` array (no cascade). It cannot be undone.","parameters":[{"schema":{"type":"string","minLength":1,"description":"The author's opaque object `id`: the `id` field returned by the author endpoints (e.g. `a4u7t9k2d6q0r3s8v1w5x9y2z6b0c4m7`). This is NOT the `slug` or name; passing a slug like `jane-doe` returns 404. To look up an author by name or slug, use `GET /v1/blog/authors` and read the returned `id`."},"required":true,"description":"The author's opaque object `id`: the `id` field returned by the author endpoints (e.g. `a4u7t9k2d6q0r3s8v1w5x9y2z6b0c4m7`). This is NOT the `slug` or name; passing a slug like `jane-doe` returns 404. To look up an author by name or slug, use `GET /v1/blog/authors` and read the returned `id`.","example":"a4u7t9k2d6q0r3s8v1w5x9y2z6b0c4m7","name":"id","in":"path"}],"responses":{"204":{"description":"Successful response."},"404":{"description":"Author not found."}},"tags":["Other"]}},"/blog/posts":{"get":{"summary":"List blog posts","description":"Returns a list of blog posts for the account, most recently updated first. Filter by a single `status`.","parameters":[{"schema":{"type":"string","minLength":1},"required":false,"name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","enum":["draft","scheduled","published"]},"required":false,"name":"status","in":"query"}],"responses":{"200":{"description":"Successful response.","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","enum":["list"]},"url":{"type":"string","example":"/v1/blog/posts"},"hasMore":{"type":"boolean"},"nextCursor":{"type":"string","nullable":true},"data":{"type":"array","items":{"$ref":"#/components/schemas/BlogPostSummary"}}},"required":["object","url","hasMore","nextCursor","data"]}}}},"400":{"description":"The request was malformed."}},"tags":["Other"]},"post":{"summary":"Create a blog post","description":"Creates a blog post. New posts default to `status: \"draft\"`; publish explicitly via `POST /v1/blog/posts/{id}/publish`. The `html` body is sanitized server-side.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateBlogPostBody"}}}},"responses":{"201":{"description":"Successful response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BlogPost"}}}},"400":{"description":"The request was malformed."}},"tags":["Other"]}},"/blog/posts/{id}":{"get":{"summary":"Retrieve a blog post","parameters":[{"schema":{"type":"string","minLength":1,"description":"The post's opaque object `id`: the `id` field returned by the blog-post endpoints (e.g. `b3p8m2k7d9q4x0r5t1v6w8y2z7a4c0n3`). This is NOT the `slug` or title; passing a slug like `my-first-post` returns 404. To look up a post by title or slug, use `GET /v1/blog/posts` or `POST /v1/blog/posts/search` and read the returned `id`."},"required":true,"description":"The post's opaque object `id`: the `id` field returned by the blog-post endpoints (e.g. `b3p8m2k7d9q4x0r5t1v6w8y2z7a4c0n3`). This is NOT the `slug` or title; passing a slug like `my-first-post` returns 404. To look up a post by title or slug, use `GET /v1/blog/posts` or `POST /v1/blog/posts/search` and read the returned `id`.","example":"b3p8m2k7d9q4x0r5t1v6w8y2z7a4c0n3","name":"id","in":"path"}],"responses":{"200":{"description":"Successful response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BlogPost"}}}},"404":{"description":"Blog post not found."}},"tags":["Other"]},"patch":{"summary":"Update a blog post","description":"Updates a blog post. A `status` value is rejected with `blog_invalid_transition` unless it is the scheduling carve-out (`status: \"scheduled\"` with a future `publishedAt`); use the publish/unpublish endpoints for other lifecycle changes.","parameters":[{"schema":{"type":"string","minLength":1,"description":"The post's opaque object `id`: the `id` field returned by the blog-post endpoints (e.g. `b3p8m2k7d9q4x0r5t1v6w8y2z7a4c0n3`). This is NOT the `slug` or title; passing a slug like `my-first-post` returns 404. To look up a post by title or slug, use `GET /v1/blog/posts` or `POST /v1/blog/posts/search` and read the returned `id`."},"required":true,"description":"The post's opaque object `id`: the `id` field returned by the blog-post endpoints (e.g. `b3p8m2k7d9q4x0r5t1v6w8y2z7a4c0n3`). This is NOT the `slug` or title; passing a slug like `my-first-post` returns 404. To look up a post by title or slug, use `GET /v1/blog/posts` or `POST /v1/blog/posts/search` and read the returned `id`.","example":"b3p8m2k7d9q4x0r5t1v6w8y2z7a4c0n3","name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateBlogPostBody"}}}},"responses":{"200":{"description":"Successful response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BlogPost"}}}},"400":{"description":"Malformed request or an invalid status transition."},"404":{"description":"Blog post not found."},"409":{"description":"The slug change collides with an existing post."}},"tags":["Other"]},"delete":{"summary":"Delete a blog post","parameters":[{"schema":{"type":"string","minLength":1,"description":"The post's opaque object `id`: the `id` field returned by the blog-post endpoints (e.g. `b3p8m2k7d9q4x0r5t1v6w8y2z7a4c0n3`). This is NOT the `slug` or title; passing a slug like `my-first-post` returns 404. To look up a post by title or slug, use `GET /v1/blog/posts` or `POST /v1/blog/posts/search` and read the returned `id`."},"required":true,"description":"The post's opaque object `id`: the `id` field returned by the blog-post endpoints (e.g. `b3p8m2k7d9q4x0r5t1v6w8y2z7a4c0n3`). This is NOT the `slug` or title; passing a slug like `my-first-post` returns 404. To look up a post by title or slug, use `GET /v1/blog/posts` or `POST /v1/blog/posts/search` and read the returned `id`.","example":"b3p8m2k7d9q4x0r5t1v6w8y2z7a4c0n3","name":"id","in":"path"}],"responses":{"204":{"description":"Successful response."},"404":{"description":"Blog post not found."}},"tags":["Other"]}},"/blog/posts/search":{"post":{"summary":"Search blog posts","description":"Lexical title search across all statuses (includes drafts). Optionally narrow to a single `status`.","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchBlogPostsBody"}}}},"responses":{"200":{"description":"Successful response.","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","enum":["search_result"]},"url":{"type":"string","example":"/v1/blog/posts/search"},"hasMore":{"type":"boolean"},"nextCursor":{"type":"string","nullable":true},"data":{"type":"array","items":{"$ref":"#/components/schemas/BlogPostSummary"}}},"required":["object","url","hasMore","nextCursor","data"]}}}}},"tags":["Other"]}},"/blog/posts/batch":{"post":{"summary":"Run a batch of blog post operations","description":"Runs up to 50 blog post operations in a single request (5 MB body limit). Each sub-operation runs independently and writes its own audit entry. A `publish`/`unpublish` sub-op uses `{ method: \"POST\", action: \"publish\", resourceId }` and requires `blog.publish`. Idempotency keys are honored on the batch itself, not on individual sub-operations.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BlogPostsBatchRequest"}}}},"responses":{"200":{"description":"Successful response. Inspect each entry of the `data` array for the per-sub-operation status.","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","enum":["batch_result"]},"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"number"},"data":{"nullable":true},"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"nullable":true}},"required":["code","message"]}},"required":["id","status"]}}},"required":["object","data"]}}}},"413":{"description":"The request body exceeded 5 MB."}},"tags":["Other"]}},"/blog/tags":{"get":{"summary":"List all blog tags","description":"Returns a list of your blog tags. The tags are returned sorted by update date, with the most recently updated tags appearing first.","parameters":[{"schema":{"type":"string","minLength":1,"description":"An opaque pagination cursor returned in the `nextCursor` field of a previous response. Pass it back to fetch the next page of results."},"required":false,"description":"An opaque pagination cursor returned in the `nextCursor` field of a previous response. Pass it back to fetch the next page of results.","name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"description":"A limit on the number of objects to be returned. Limit can range between 1 and 100."},"required":false,"description":"A limit on the number of objects to be returned. Limit can range between 1 and 100.","example":20,"name":"limit","in":"query"}],"responses":{"200":{"description":"Successful response.","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","enum":["list"]},"url":{"type":"string","example":"/v1/blog/tags"},"hasMore":{"type":"boolean"},"nextCursor":{"type":"string","nullable":true},"data":{"type":"array","items":{"$ref":"#/components/schemas/BlogTag"}}},"required":["object","url","hasMore","nextCursor","data"]}}}},"400":{"description":"The request was malformed."}},"tags":["Other"]},"post":{"summary":"Create a blog tag","description":"Creates a new blog tag. The request is idempotent when an `Idempotency-Key` header is supplied.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTagBody"}}}},"responses":{"201":{"description":"Successful response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BlogTag"}}}},"400":{"description":"The request was malformed."},"409":{"description":"Another tag already uses the same slug."}},"tags":["Other"]}},"/blog/tags/{id}":{"patch":{"summary":"Update a blog tag","description":"Updates the specified blog tag by setting the values of the parameters passed. Any parameters not provided will be left unchanged. The request is idempotent when an `Idempotency-Key` header is supplied.","parameters":[{"schema":{"type":"string","minLength":1,"description":"The tag's opaque object `id`: the `id` field returned by the tag endpoints (e.g. `t1g5k9d3q7m0p4r8s2v6w1x5y9z3b7c0`). This is NOT the `slug` or name; passing a slug like `engineering` returns 404. To look up a tag by name or slug, use `GET /v1/blog/tags` and read the returned `id`."},"required":true,"description":"The tag's opaque object `id`: the `id` field returned by the tag endpoints (e.g. `t1g5k9d3q7m0p4r8s2v6w1x5y9z3b7c0`). This is NOT the `slug` or name; passing a slug like `engineering` returns 404. To look up a tag by name or slug, use `GET /v1/blog/tags` and read the returned `id`.","example":"t1g5k9d3q7m0p4r8s2v6w1x5y9z3b7c0","name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateTagBody"}}}},"responses":{"200":{"description":"Successful response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BlogTag"}}}},"400":{"description":"The request was malformed."},"404":{"description":"Tag not found."},"409":{"description":"Another tag already uses the same slug."}},"tags":["Other"]},"delete":{"summary":"Delete a blog tag","description":"Permanently deletes a blog tag. Posts referencing this tag keep the stale ID in their `tagIds` array (no cascade). It cannot be undone.","parameters":[{"schema":{"type":"string","minLength":1,"description":"The tag's opaque object `id`: the `id` field returned by the tag endpoints (e.g. `t1g5k9d3q7m0p4r8s2v6w1x5y9z3b7c0`). This is NOT the `slug` or name; passing a slug like `engineering` returns 404. To look up a tag by name or slug, use `GET /v1/blog/tags` and read the returned `id`."},"required":true,"description":"The tag's opaque object `id`: the `id` field returned by the tag endpoints (e.g. `t1g5k9d3q7m0p4r8s2v6w1x5y9z3b7c0`). This is NOT the `slug` or name; passing a slug like `engineering` returns 404. To look up a tag by name or slug, use `GET /v1/blog/tags` and read the returned `id`.","example":"t1g5k9d3q7m0p4r8s2v6w1x5y9z3b7c0","name":"id","in":"path"}],"responses":{"204":{"description":"Successful response."},"404":{"description":"Tag not found."}},"tags":["Other"]}},"/boards/{identifier}/blog/posts":{"get":{"security":[],"summary":"List a public board's blog posts","description":"Returns a list of published blog posts for the named public board, most recently published first. Optionally filter by `tagSlug`, `authorSlug`, or `featured`.","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":150,"description":"Board identifier, prefix-discriminated (ADR-0032): the board slug (mutable), a `boards_…` board ID (immutable), or a `pk_…` publishable key (immutable, revocable). Headless frontends should bind to `boards_…` or `pk_…` — slugs can be renamed by the operator."},"required":true,"description":"Board identifier, prefix-discriminated (ADR-0032): the board slug (mutable), a `boards_…` board ID (immutable), or a `pk_…` publishable key (immutable, revocable). Headless frontends should bind to `boards_…` or `pk_…` — slugs can be renamed by the operator.","name":"identifier","in":"path"},{"schema":{"type":"string","minLength":1,"description":"An opaque pagination cursor returned in the `nextCursor` field of a previous response. Pass it back to fetch the next page of results."},"required":false,"description":"An opaque pagination cursor returned in the `nextCursor` field of a previous response. Pass it back to fetch the next page of results.","name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"description":"A limit on the number of objects to be returned. Limit can range between 1 and 100."},"required":false,"description":"A limit on the number of objects to be returned. Limit can range between 1 and 100.","example":25,"name":"limit","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":200,"description":"Restrict the list to posts tagged with this tag slug."},"required":false,"description":"Restrict the list to posts tagged with this tag slug.","name":"tagSlug","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":200,"description":"Restrict the list to posts by this author slug."},"required":false,"description":"Restrict the list to posts by this author slug.","name":"authorSlug","in":"query"},{"schema":{"type":"string","enum":["true"],"description":"Pass `true` to restrict the list to featured posts."},"required":false,"description":"Pass `true` to restrict the list to featured posts.","name":"featured","in":"query"}],"responses":{"200":{"description":"Successful response.","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","enum":["list"]},"url":{"type":"string"},"hasMore":{"type":"boolean"},"nextCursor":{"type":"string","nullable":true},"data":{"type":"array","items":{"$ref":"#/components/schemas/PublicBlogPostSummary"}}},"required":["object","url","hasMore","nextCursor","data"]}}}},"404":{"description":"Public board not found, or the board is private."}},"tags":["Other"]}},"/boards/{identifier}/blog/posts/{postSlug}":{"get":{"security":[],"summary":"Retrieve a public blog post","description":"Retrieves a published blog post on the named public board by its slug, including the rendered `html` body and embedded authors/tags.","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":150,"description":"Board identifier, prefix-discriminated (ADR-0032): the board slug (mutable), a `boards_…` board ID (immutable), or a `pk_…` publishable key (immutable, revocable). Headless frontends should bind to `boards_…` or `pk_…` — slugs can be renamed by the operator."},"required":true,"description":"Board identifier, prefix-discriminated (ADR-0032): the board slug (mutable), a `boards_…` board ID (immutable), or a `pk_…` publishable key (immutable, revocable). Headless frontends should bind to `boards_…` or `pk_…` — slugs can be renamed by the operator.","name":"identifier","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":300,"description":"URL slug of the blog post."},"required":true,"description":"URL slug of the blog post.","name":"postSlug","in":"path"}],"responses":{"200":{"description":"Successful response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicBlogPost"}}}},"404":{"description":"Public board or blog post not found."}},"tags":["Other"]}},"/boards/{identifier}/blog/search":{"post":{"security":[],"summary":"Search a public board's blog posts","description":"Searches published blog posts on the named public board using a free-text query. Results carry the same fields as the posts list.","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":150,"description":"Board identifier, prefix-discriminated (ADR-0032): the board slug (mutable), a `boards_…` board ID (immutable), or a `pk_…` publishable key (immutable, revocable). Headless frontends should bind to `boards_…` or `pk_…` — slugs can be renamed by the operator."},"required":true,"description":"Board identifier, prefix-discriminated (ADR-0032): the board slug (mutable), a `boards_…` board ID (immutable), or a `pk_…` publishable key (immutable, revocable). Headless frontends should bind to `boards_…` or `pk_…` — slugs can be renamed by the operator.","name":"identifier","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicBlogSearchBody"}}}},"responses":{"200":{"description":"Successful response.","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","enum":["search_result"]},"url":{"type":"string"},"hasMore":{"type":"boolean"},"nextCursor":{"type":"string","nullable":true},"data":{"type":"array","items":{"$ref":"#/components/schemas/PublicBlogPostSummary"}}},"required":["object","url","hasMore","nextCursor","data"]}}}},"404":{"description":"Public board not found, or the board is private."}},"tags":["Other"]}},"/boards/{identifier}/blog/tags":{"get":{"security":[],"summary":"List a public board's blog tags","description":"Returns the public (non-internal) blog tags for the named public board.","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":150,"description":"Board identifier, prefix-discriminated (ADR-0032): the board slug (mutable), a `boards_…` board ID (immutable), or a `pk_…` publishable key (immutable, revocable). Headless frontends should bind to `boards_…` or `pk_…` — slugs can be renamed by the operator."},"required":true,"description":"Board identifier, prefix-discriminated (ADR-0032): the board slug (mutable), a `boards_…` board ID (immutable), or a `pk_…` publishable key (immutable, revocable). Headless frontends should bind to `boards_…` or `pk_…` — slugs can be renamed by the operator.","name":"identifier","in":"path"}],"responses":{"200":{"description":"Successful response.","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","enum":["list"]},"url":{"type":"string"},"hasMore":{"type":"boolean"},"nextCursor":{"type":"string","nullable":true},"data":{"type":"array","items":{"$ref":"#/components/schemas/PublicBlogTag"}}},"required":["object","url","hasMore","nextCursor","data"]}}}},"404":{"description":"Public board not found, or the board is private."}},"tags":["Other"]}},"/boards/{identifier}/blog/tags/{tagSlug}":{"get":{"security":[],"summary":"Retrieve a public blog tag","description":"Retrieves a public blog tag on the named public board by its slug.","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":150,"description":"Board identifier, prefix-discriminated (ADR-0032): the board slug (mutable), a `boards_…` board ID (immutable), or a `pk_…` publishable key (immutable, revocable). Headless frontends should bind to `boards_…` or `pk_…` — slugs can be renamed by the operator."},"required":true,"description":"Board identifier, prefix-discriminated (ADR-0032): the board slug (mutable), a `boards_…` board ID (immutable), or a `pk_…` publishable key (immutable, revocable). Headless frontends should bind to `boards_…` or `pk_…` — slugs can be renamed by the operator.","name":"identifier","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":200,"description":"URL slug of the blog tag."},"required":true,"description":"URL slug of the blog tag.","name":"tagSlug","in":"path"}],"responses":{"200":{"description":"Successful response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicBlogTag"}}}},"404":{"description":"Public board or blog tag not found."}},"tags":["Other"]}},"/boards/{identifier}/blog/authors":{"get":{"security":[],"summary":"List a public board's blog authors","description":"Returns the active blog authors for the named public board. Author avatars are resolved on the detail read, not the list.","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":150,"description":"Board identifier, prefix-discriminated (ADR-0032): the board slug (mutable), a `boards_…` board ID (immutable), or a `pk_…` publishable key (immutable, revocable). Headless frontends should bind to `boards_…` or `pk_…` — slugs can be renamed by the operator."},"required":true,"description":"Board identifier, prefix-discriminated (ADR-0032): the board slug (mutable), a `boards_…` board ID (immutable), or a `pk_…` publishable key (immutable, revocable). Headless frontends should bind to `boards_…` or `pk_…` — slugs can be renamed by the operator.","name":"identifier","in":"path"}],"responses":{"200":{"description":"Successful response.","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","enum":["list"]},"url":{"type":"string"},"hasMore":{"type":"boolean"},"nextCursor":{"type":"string","nullable":true},"data":{"type":"array","items":{"$ref":"#/components/schemas/PublicBlogAuthor"}}},"required":["object","url","hasMore","nextCursor","data"]}}}},"404":{"description":"Public board not found, or the board is private."}},"tags":["Other"]}},"/boards/{identifier}/blog/authors/{authorSlug}":{"get":{"security":[],"summary":"Retrieve a public blog author","description":"Retrieves an active blog author on the named public board by its slug.","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":150,"description":"Board identifier, prefix-discriminated (ADR-0032): the board slug (mutable), a `boards_…` board ID (immutable), or a `pk_…` publishable key (immutable, revocable). Headless frontends should bind to `boards_…` or `pk_…` — slugs can be renamed by the operator."},"required":true,"description":"Board identifier, prefix-discriminated (ADR-0032): the board slug (mutable), a `boards_…` board ID (immutable), or a `pk_…` publishable key (immutable, revocable). Headless frontends should bind to `boards_…` or `pk_…` — slugs can be renamed by the operator.","name":"identifier","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":200,"description":"URL slug of the blog author."},"required":true,"description":"URL slug of the blog author.","name":"authorSlug","in":"path"}],"responses":{"200":{"description":"Successful response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicBlogAuthor"}}}},"404":{"description":"Public board or blog author not found."}},"tags":["Other"]}},"/companies":{"get":{"summary":"List all companies","description":"Returns a list of your companies. The companies are returned sorted by creation date, with the most recently created companies appearing first.","parameters":[{"schema":{"type":"string","minLength":1,"description":"An opaque pagination cursor returned in the `nextCursor` field of a previous response. Pass it back to fetch the next page of results."},"required":false,"description":"An opaque pagination cursor returned in the `nextCursor` field of a previous response. Pass it back to fetch the next page of results.","name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"description":"A limit on the number of objects to be returned. Limit can range between 1 and 100."},"required":false,"description":"A limit on the number of objects to be returned. Limit can range between 1 and 100.","example":20,"name":"limit","in":"query"}],"responses":{"200":{"description":"Successful response.","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","enum":["list"]},"url":{"type":"string","example":"/v1/companies"},"hasMore":{"type":"boolean"},"nextCursor":{"type":"string","nullable":true},"data":{"type":"array","items":{"$ref":"#/components/schemas/CompanySummary"}}},"required":["object","url","hasMore","nextCursor","data"]}}}},"400":{"description":"The request was malformed."}},"tags":["Companies"]},"post":{"summary":"Create a company","description":"Creates a new company. A logo is auto-fetched from `website` when available. The request is idempotent when an `Idempotency-Key` header is supplied.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCompanyBody"}}}},"responses":{"201":{"description":"Successful response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Company"}}}},"400":{"description":"The request was malformed."},"409":{"description":"Another company already uses the same website domain or name."}},"tags":["Companies"]}},"/companies/find-or-create":{"post":{"summary":"Resolve or create a company","description":"Resolves an existing company by website domain (and optionally by name), or creates a new one if no match is found. On creation, a logo is auto-fetched from `website` when available. Use `matchByName` to disable name fallback, or `createIfMissing` to disable creation. Useful for workflows that have a name and website but no company ID yet.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FindOrCreateCompanyBody"}}}},"responses":{"200":{"description":"Successful response. The `matched` field indicates whether the returned company already existed (`true`) or was newly created (`false`).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Company"},{"type":"object","properties":{"matched":{"type":"boolean"}},"required":["matched"]}]}}}},"404":{"description":"No company matched the supplied `name` or `website`, and `createIfMissing` was set to `false`."}},"tags":["Companies"]}},"/companies/{id}":{"get":{"summary":"Retrieve a company","description":"Retrieves the details of an existing company. You only need to supply the unique company ID that was returned upon company creation.","parameters":[{"schema":{"type":"string","minLength":1,"description":"The company's opaque object `id`: the `id` field returned by the company endpoints (e.g. `p17xq4c2b9r5gz3e8j0r6t4k7m2n9d5w`). This is NOT the `slug` or display name; passing a slug like `antler` returns 404. To look up a company by name or slug, use `GET /v1/companies`, `POST /v1/companies/search`, or `POST /v1/companies/find-or-create` and read the returned `id`."},"required":true,"description":"The company's opaque object `id`: the `id` field returned by the company endpoints (e.g. `p17xq4c2b9r5gz3e8j0r6t4k7m2n9d5w`). This is NOT the `slug` or display name; passing a slug like `antler` returns 404. To look up a company by name or slug, use `GET /v1/companies`, `POST /v1/companies/search`, or `POST /v1/companies/find-or-create` and read the returned `id`.","example":"p17xq4c2b9r5gz3e8j0r6t4k7m2n9d5w","name":"id","in":"path"}],"responses":{"200":{"description":"Successful response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Company"}}}},"404":{"description":"Company not found."}},"tags":["Companies"]},"patch":{"summary":"Update a company","description":"Updates the specified company by setting the values of the parameters passed. Any parameters not provided will be left unchanged. The request is idempotent when an `Idempotency-Key` header is supplied.","parameters":[{"schema":{"type":"string","minLength":1,"description":"The company's opaque object `id`: the `id` field returned by the company endpoints (e.g. `p17xq4c2b9r5gz3e8j0r6t4k7m2n9d5w`). This is NOT the `slug` or display name; passing a slug like `antler` returns 404. To look up a company by name or slug, use `GET /v1/companies`, `POST /v1/companies/search`, or `POST /v1/companies/find-or-create` and read the returned `id`."},"required":true,"description":"The company's opaque object `id`: the `id` field returned by the company endpoints (e.g. `p17xq4c2b9r5gz3e8j0r6t4k7m2n9d5w`). This is NOT the `slug` or display name; passing a slug like `antler` returns 404. To look up a company by name or slug, use `GET /v1/companies`, `POST /v1/companies/search`, or `POST /v1/companies/find-or-create` and read the returned `id`.","example":"p17xq4c2b9r5gz3e8j0r6t4k7m2n9d5w","name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateCompanyBody"}}}},"responses":{"200":{"description":"Successful response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Company"}}}},"400":{"description":"The request was malformed."},"404":{"description":"Company not found."},"409":{"description":"Another company already uses the same website domain or name."}},"tags":["Companies"]},"delete":{"summary":"Delete a company","description":"Permanently deletes a company and cascades to delete every job attached to it (along with each job’s child records). It cannot be undone.","parameters":[{"schema":{"type":"string","minLength":1,"description":"The company's opaque object `id`: the `id` field returned by the company endpoints (e.g. `p17xq4c2b9r5gz3e8j0r6t4k7m2n9d5w`). This is NOT the `slug` or display name; passing a slug like `antler` returns 404. To look up a company by name or slug, use `GET /v1/companies`, `POST /v1/companies/search`, or `POST /v1/companies/find-or-create` and read the returned `id`."},"required":true,"description":"The company's opaque object `id`: the `id` field returned by the company endpoints (e.g. `p17xq4c2b9r5gz3e8j0r6t4k7m2n9d5w`). This is NOT the `slug` or display name; passing a slug like `antler` returns 404. To look up a company by name or slug, use `GET /v1/companies`, `POST /v1/companies/search`, or `POST /v1/companies/find-or-create` and read the returned `id`.","example":"p17xq4c2b9r5gz3e8j0r6t4k7m2n9d5w","name":"id","in":"path"}],"responses":{"204":{"description":"Successful response."},"404":{"description":"Company not found."}},"tags":["Companies"]}},"/companies/{id}/logo":{"post":{"summary":"Upload a company logo","description":"Uploads a logo for the company as a multipart form with a single `file` part. Accepts PNG, JPEG, WebP, and GIF up to 5 MB. Replaces the previous logo when one exists.","parameters":[{"schema":{"type":"string","minLength":1,"description":"The company's opaque object `id`: the `id` field returned by the company endpoints (e.g. `p17xq4c2b9r5gz3e8j0r6t4k7m2n9d5w`). This is NOT the `slug` or display name; passing a slug like `antler` returns 404. To look up a company by name or slug, use `GET /v1/companies`, `POST /v1/companies/search`, or `POST /v1/companies/find-or-create` and read the returned `id`."},"required":true,"description":"The company's opaque object `id`: the `id` field returned by the company endpoints (e.g. `p17xq4c2b9r5gz3e8j0r6t4k7m2n9d5w`). This is NOT the `slug` or display name; passing a slug like `antler` returns 404. To look up a company by name or slug, use `GET /v1/companies`, `POST /v1/companies/search`, or `POST /v1/companies/find-or-create` and read the returned `id`.","example":"p17xq4c2b9r5gz3e8j0r6t4k7m2n9d5w","name":"id","in":"path"}],"requestBody":{"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary"}},"required":["file"]}}}},"responses":{"201":{"description":"Successful response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Company"}}}},"400":{"description":"The request was malformed."},"404":{"description":"Company not found."},"413":{"description":"The uploaded file exceeded the 5 MB size limit."},"415":{"description":"The uploaded file's MIME type is not supported."}},"tags":["Companies"]},"delete":{"summary":"Delete a company logo","description":"Removes the logo from the company and deletes the underlying file. Returns 404 if the company has no logo set.","parameters":[{"schema":{"type":"string","minLength":1,"description":"The company's opaque object `id`: the `id` field returned by the company endpoints (e.g. `p17xq4c2b9r5gz3e8j0r6t4k7m2n9d5w`). This is NOT the `slug` or display name; passing a slug like `antler` returns 404. To look up a company by name or slug, use `GET /v1/companies`, `POST /v1/companies/search`, or `POST /v1/companies/find-or-create` and read the returned `id`."},"required":true,"description":"The company's opaque object `id`: the `id` field returned by the company endpoints (e.g. `p17xq4c2b9r5gz3e8j0r6t4k7m2n9d5w`). This is NOT the `slug` or display name; passing a slug like `antler` returns 404. To look up a company by name or slug, use `GET /v1/companies`, `POST /v1/companies/search`, or `POST /v1/companies/find-or-create` and read the returned `id`.","example":"p17xq4c2b9r5gz3e8j0r6t4k7m2n9d5w","name":"id","in":"path"}],"responses":{"204":{"description":"Successful response."},"404":{"description":"Company not found, or the company has no logo to remove."}},"tags":["Companies"]}},"/companies/{id}/jobs":{"get":{"summary":"List jobs at a company","description":"Returns a list of jobs belonging to the specified company. The jobs are returned sorted by creation date, with the most recently created jobs appearing first.","parameters":[{"schema":{"type":"string","minLength":1,"description":"The company's opaque object `id`: the `id` field returned by the company endpoints (e.g. `p17xq4c2b9r5gz3e8j0r6t4k7m2n9d5w`). This is NOT the `slug` or display name; passing a slug like `antler` returns 404. To look up a company by name or slug, use `GET /v1/companies`, `POST /v1/companies/search`, or `POST /v1/companies/find-or-create` and read the returned `id`."},"required":true,"description":"The company's opaque object `id`: the `id` field returned by the company endpoints (e.g. `p17xq4c2b9r5gz3e8j0r6t4k7m2n9d5w`). This is NOT the `slug` or display name; passing a slug like `antler` returns 404. To look up a company by name or slug, use `GET /v1/companies`, `POST /v1/companies/search`, or `POST /v1/companies/find-or-create` and read the returned `id`.","example":"p17xq4c2b9r5gz3e8j0r6t4k7m2n9d5w","name":"id","in":"path"},{"schema":{"type":"string","minLength":1,"description":"An opaque pagination cursor returned in the `nextCursor` field of a previous response. Pass it back to fetch the next page of results."},"required":false,"description":"An opaque pagination cursor returned in the `nextCursor` field of a previous response. Pass it back to fetch the next page of results.","name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"description":"A limit on the number of objects to be returned. Limit can range between 1 and 100."},"required":false,"description":"A limit on the number of objects to be returned. Limit can range between 1 and 100.","example":20,"name":"limit","in":"query"},{"schema":{"type":"string","enum":["draft","published","expired","archived"],"description":"Only return jobs matching the given status. One of `draft`, `published`, `expired`, or `archived`."},"required":false,"description":"Only return jobs matching the given status. One of `draft`, `published`, `expired`, or `archived`.","example":"published","name":"status","in":"query"}],"responses":{"200":{"description":"Successful response.","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","enum":["list"]},"url":{"type":"string","example":"/v1/companies/{id}/jobs"},"hasMore":{"type":"boolean"},"nextCursor":{"type":"string","nullable":true},"data":{"type":"array","items":{"nullable":true}}},"required":["object","url","hasMore","nextCursor","data"]}}}},"404":{"description":"Company not found."}},"tags":["Companies"]}},"/companies/search":{"post":{"summary":"Search companies","description":"Searches your companies using a free-text query. Results are returned sorted by creation date, with the most recently created companies appearing first.","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchCompaniesBody"}}}},"responses":{"200":{"description":"Successful response.","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","enum":["search_result"]},"url":{"type":"string","example":"/v1/companies/search"},"hasMore":{"type":"boolean"},"nextCursor":{"type":"string","nullable":true},"data":{"type":"array","items":{"$ref":"#/components/schemas/Company"}}},"required":["object","url","hasMore","nextCursor","data"]}}}}},"tags":["Companies"]}},"/boards/{identifier}/companies":{"get":{"security":[],"summary":"List a public board's companies","description":"Returns a list of companies on the named public board. The companies are returned sorted by creation date, with the most recently created companies appearing first.","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":150,"description":"Board identifier, prefix-discriminated (ADR-0032): the board slug (mutable), a `boards_…` board ID (immutable), or a `pk_…` publishable key (immutable, revocable). Headless frontends should bind to `boards_…` or `pk_…` — slugs can be renamed by the operator."},"required":true,"description":"Board identifier, prefix-discriminated (ADR-0032): the board slug (mutable), a `boards_…` board ID (immutable), or a `pk_…` publishable key (immutable, revocable). Headless frontends should bind to `boards_…` or `pk_…` — slugs can be renamed by the operator.","name":"identifier","in":"path"},{"schema":{"type":"string","minLength":1,"description":"An opaque pagination cursor returned in the `nextCursor` field of a previous response. Pass it back to fetch the next page of results."},"required":false,"description":"An opaque pagination cursor returned in the `nextCursor` field of a previous response. Pass it back to fetch the next page of results.","name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"description":"A limit on the number of objects to be returned. Limit can range between 1 and 100."},"required":false,"description":"A limit on the number of objects to be returned. Limit can range between 1 and 100.","example":20,"name":"limit","in":"query"}],"responses":{"200":{"description":"Successful response.","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","enum":["list"]},"url":{"type":"string"},"hasMore":{"type":"boolean"},"nextCursor":{"type":"string","nullable":true},"data":{"type":"array","items":{"$ref":"#/components/schemas/CompanyPublic"}}},"required":["object","url","hasMore","nextCursor","data"]}}}},"404":{"description":"Public board not found, or the board is private."}},"tags":["Other"]}},"/boards/{identifier}/companies/{companySlug}":{"get":{"security":[],"summary":"Retrieve a public company","description":"Retrieves a company on the named public board by its slug.","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":150,"description":"Board identifier, prefix-discriminated (ADR-0032): the board slug (mutable), a `boards_…` board ID (immutable), or a `pk_…` publishable key (immutable, revocable). Headless frontends should bind to `boards_…` or `pk_…` — slugs can be renamed by the operator."},"required":true,"description":"Board identifier, prefix-discriminated (ADR-0032): the board slug (mutable), a `boards_…` board ID (immutable), or a `pk_…` publishable key (immutable, revocable). Headless frontends should bind to `boards_…` or `pk_…` — slugs can be renamed by the operator.","name":"identifier","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":150,"description":"URL slug of the company."},"required":true,"description":"URL slug of the company.","name":"companySlug","in":"path"}],"responses":{"200":{"description":"Successful response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompanyPublic"}}}},"404":{"description":"Public board or company not found."}},"tags":["Other"]}},"/boards/{identifier}/companies/search":{"post":{"security":[],"summary":"Search a public board's companies","description":"Searches companies on the named public board using a free-text query. Search results return a subset of fields — retrieve the full company resource via `GET /v1/boards/{identifier}/companies/{companySlug}` if you need additional fields.","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":150,"description":"Board identifier, prefix-discriminated (ADR-0032): the board slug (mutable), a `boards_…` board ID (immutable), or a `pk_…` publishable key (immutable, revocable). Headless frontends should bind to `boards_…` or `pk_…` — slugs can be renamed by the operator."},"required":true,"description":"Board identifier, prefix-discriminated (ADR-0032): the board slug (mutable), a `boards_…` board ID (immutable), or a `pk_…` publishable key (immutable, revocable). Headless frontends should bind to `boards_…` or `pk_…` — slugs can be renamed by the operator.","name":"identifier","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicCompaniesSearchBody"}}}},"responses":{"200":{"description":"Successful response.","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","enum":["search_result"]},"url":{"type":"string"},"hasMore":{"type":"boolean"},"nextCursor":{"type":"string","nullable":true},"data":{"type":"array","items":{"$ref":"#/components/schemas/CompanyPublic"}}},"required":["object","url","hasMore","nextCursor","data"]}}}}},"tags":["Other"]}},"/boards/{identifier}/companies/{companySlug}/jobs":{"get":{"security":[],"summary":"List published jobs at a public company","description":"Returns a list of published jobs for a single company on the named public board. The jobs are returned sorted by creation date, with the most recently created jobs appearing first.","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":150,"description":"Board identifier, prefix-discriminated (ADR-0032): the board slug (mutable), a `boards_…` board ID (immutable), or a `pk_…` publishable key (immutable, revocable). Headless frontends should bind to `boards_…` or `pk_…` — slugs can be renamed by the operator."},"required":true,"description":"Board identifier, prefix-discriminated (ADR-0032): the board slug (mutable), a `boards_…` board ID (immutable), or a `pk_…` publishable key (immutable, revocable). Headless frontends should bind to `boards_…` or `pk_…` — slugs can be renamed by the operator.","name":"identifier","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":150,"description":"URL slug of the company."},"required":true,"description":"URL slug of the company.","name":"companySlug","in":"path"},{"schema":{"type":"string","minLength":1,"description":"An opaque pagination cursor returned in the `nextCursor` field of a previous response. Pass it back to fetch the next page of results."},"required":false,"description":"An opaque pagination cursor returned in the `nextCursor` field of a previous response. Pass it back to fetch the next page of results.","name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"description":"A limit on the number of objects to be returned. Limit can range between 1 and 100."},"required":false,"description":"A limit on the number of objects to be returned. Limit can range between 1 and 100.","example":20,"name":"limit","in":"query"}],"responses":{"200":{"description":"Successful response.","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","enum":["list"]},"url":{"type":"string"},"hasMore":{"type":"boolean"},"nextCursor":{"type":"string","nullable":true},"data":{"type":"array","items":{"nullable":true}}},"required":["object","url","hasMore","nextCursor","data"]}}}},"404":{"description":"Public board or company not found."}},"tags":["Other"]}},"/jobs":{"get":{"summary":"List all jobs","description":"Returns a list of your jobs. The jobs are returned sorted by creation date, with the most recently created jobs appearing first.","parameters":[{"schema":{"type":"string","minLength":1,"description":"An opaque pagination cursor returned in the `nextCursor` field of a previous response. Pass it back to fetch the next page of results."},"required":false,"description":"An opaque pagination cursor returned in the `nextCursor` field of a previous response. Pass it back to fetch the next page of results.","name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"description":"A limit on the number of objects to be returned. Limit can range between 1 and 100."},"required":false,"description":"A limit on the number of objects to be returned. Limit can range between 1 and 100.","example":20,"name":"limit","in":"query"},{"schema":{"type":"string","enum":["draft","published","expired","archived"],"description":"Only return jobs matching the given status. One of `draft`, `published`, `expired`, or `archived`."},"required":false,"description":"Only return jobs matching the given status. One of `draft`, `published`, `expired`, or `archived`.","example":"published","name":"status","in":"query"},{"schema":{"type":"string","minLength":1,"description":"Only return jobs at the company with the given ID."},"required":false,"description":"Only return jobs at the company with the given ID.","name":"companyId","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":2048,"description":"Only return jobs matching this application URL. URLs are normalized server-side (casing, tracking params, etc.) so equivalent links match. Useful for deduplication lookups."},"required":false,"description":"Only return jobs matching this application URL. URLs are normalized server-side (casing, tracking params, etc.) so equivalent links match. Useful for deduplication lookups.","name":"applicationUrl","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":255,"description":"Only return jobs with this `externalId`. Useful for deduplication lookups when reposting from an ATS."},"required":false,"description":"Only return jobs with this `externalId`. Useful for deduplication lookups when reposting from an ATS.","name":"externalId","in":"query"}],"responses":{"200":{"description":"Successful response.","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","enum":["list"]},"url":{"type":"string","example":"/v1/jobs"},"hasMore":{"type":"boolean"},"nextCursor":{"type":"string","nullable":true},"data":{"type":"array","items":{"$ref":"#/components/schemas/JobSummary"}}},"required":["object","url","hasMore","nextCursor","data"]}}}},"400":{"description":"The request was malformed."}},"tags":["Jobs"]},"post":{"summary":"Create a job","description":"Creates a new job in `draft` status. The request is idempotent when an `Idempotency-Key` header is supplied. Reposts of existing jobs return 409 `jobs_already_exists` with the matching job ID.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateJobBody"}}}},"responses":{"201":{"description":"Successful response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Job"}}}},"400":{"description":"The request was malformed."},"404":{"description":"The supplied company does not exist or is not accessible."},"409":{"description":"A job with the same `applicationUrl`, `externalId`, or content already exists on your account. The matching job ID is returned in `details.existing_job_id`."}},"tags":["Jobs"]}},"/boards/{identifier}/jobs":{"get":{"security":[],"summary":"List a public board's jobs","description":"Returns a list of published jobs for the named public board. The jobs are returned sorted by creation date, with the most recently created jobs appearing first.","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":150,"description":"Board identifier, prefix-discriminated (ADR-0032): the board slug (mutable), a `boards_…` board ID (immutable), or a `pk_…` publishable key (immutable, revocable). Headless frontends should bind to `boards_…` or `pk_…` — slugs can be renamed by the operator."},"required":true,"description":"Board identifier, prefix-discriminated (ADR-0032): the board slug (mutable), a `boards_…` board ID (immutable), or a `pk_…` publishable key (immutable, revocable). Headless frontends should bind to `boards_…` or `pk_…` — slugs can be renamed by the operator.","name":"identifier","in":"path"},{"schema":{"type":"string","minLength":1,"description":"An opaque pagination cursor returned in the `nextCursor` field of a previous response. Pass it back to fetch the next page of results."},"required":false,"description":"An opaque pagination cursor returned in the `nextCursor` field of a previous response. Pass it back to fetch the next page of results.","name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"description":"A limit on the number of objects to be returned. Limit can range between 1 and 100."},"required":false,"description":"A limit on the number of objects to be returned. Limit can range between 1 and 100.","example":20,"name":"limit","in":"query"},{"schema":{"type":"string","minLength":1,"description":"Only return jobs at the company with the given ID."},"required":false,"description":"Only return jobs at the company with the given ID.","name":"companyId","in":"query"},{"schema":{"type":"string","enum":["on_site","hybrid","remote"],"description":"Only return jobs with the given remote-work option."},"required":false,"description":"Only return jobs with the given remote-work option.","name":"remoteOption","in":"query"},{"schema":{"type":"string","enum":["full_time","part_time","contract","internship","temporary","volunteer","other"],"description":"Only return jobs with the given employment type."},"required":false,"description":"Only return jobs with the given employment type.","name":"employmentType","in":"query"},{"schema":{"type":"string","enum":["entry_level","associate","mid_level","senior","lead","principal","director","executive"],"description":"Only return jobs at the given seniority level."},"required":false,"description":"Only return jobs at the given seniority level.","name":"seniority","in":"query"}],"responses":{"200":{"description":"Successful response.","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","enum":["list"]},"url":{"type":"string"},"hasMore":{"type":"boolean"},"nextCursor":{"type":"string","nullable":true},"data":{"type":"array","items":{"$ref":"#/components/schemas/PublicJob"}}},"required":["object","url","hasMore","nextCursor","data"]}}}},"404":{"description":"Public board not found, or the board is private."}},"tags":["Other"]}},"/boards/{identifier}/jobs/{jobSlug}":{"get":{"security":[],"summary":"Retrieve a public job by slug","description":"Retrieves a published job by its slug on the named public board. Only published jobs are returned.","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":150,"description":"Board identifier, prefix-discriminated (ADR-0032): the board slug (mutable), a `boards_…` board ID (immutable), or a `pk_…` publishable key (immutable, revocable). Headless frontends should bind to `boards_…` or `pk_…` — slugs can be renamed by the operator."},"required":true,"description":"Board identifier, prefix-discriminated (ADR-0032): the board slug (mutable), a `boards_…` board ID (immutable), or a `pk_…` publishable key (immutable, revocable). Headless frontends should bind to `boards_…` or `pk_…` — slugs can be renamed by the operator.","name":"identifier","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":150,"description":"URL slug of the job."},"required":true,"description":"URL slug of the job.","name":"jobSlug","in":"path"}],"responses":{"200":{"description":"Successful response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicJob"}}}},"404":{"description":"Public board not found, or the supplied job slug does not resolve to a published job on the board."}},"tags":["Other"]}},"/boards/{identifier}/jobs/search":{"post":{"security":[],"summary":"Search a public board's jobs","description":"Searches published jobs on the named public board using a free-text query and faceted filters. Results are returned sorted by creation date, with the most recently created jobs appearing first. Each filter array accepts up to 10 values.","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":150,"description":"Board identifier, prefix-discriminated (ADR-0032): the board slug (mutable), a `boards_…` board ID (immutable), or a `pk_…` publishable key (immutable, revocable). Headless frontends should bind to `boards_…` or `pk_…` — slugs can be renamed by the operator."},"required":true,"description":"Board identifier, prefix-discriminated (ADR-0032): the board slug (mutable), a `boards_…` board ID (immutable), or a `pk_…` publishable key (immutable, revocable). Headless frontends should bind to `boards_…` or `pk_…` — slugs can be renamed by the operator.","name":"identifier","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicSearchJobsBody"}}}},"responses":{"200":{"description":"Successful response.","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","enum":["search_result"]},"url":{"type":"string"},"hasMore":{"type":"boolean"},"nextCursor":{"type":"string","nullable":true},"data":{"type":"array","items":{"$ref":"#/components/schemas/PublicJob"}}},"required":["object","url","hasMore","nextCursor","data"]}}}},"400":{"description":"The request was malformed, or a filter array exceeded the per-array cap."},"404":{"description":"Public board not found, or the board is private."}},"tags":["Other"]}},"/boards/{identifier}/me/saved-jobs":{"get":{"security":[{"boardUserBearer":[]}],"summary":"List saved jobs","description":"Cursor-paginated list of the authenticated board user’s saved jobs, most recently saved first. Each row embeds the same `public_job` object the jobs list returns. Rows whose job was deleted are omitted, so a page can transiently hold fewer than `limit` items while a deletion cascade completes. Never cached.","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":150,"description":"Board identifier, prefix-discriminated (ADR-0032): the board slug (mutable), a `boards_…` board ID (immutable), or a `pk_…` publishable key (immutable, revocable). Headless frontends should bind to `boards_…` or `pk_…` — slugs can be renamed by the operator."},"required":true,"description":"Board identifier, prefix-discriminated (ADR-0032): the board slug (mutable), a `boards_…` board ID (immutable), or a `pk_…` publishable key (immutable, revocable). Headless frontends should bind to `boards_…` or `pk_…` — slugs can be renamed by the operator.","name":"identifier","in":"path"},{"schema":{"type":"string","minLength":1},"required":false,"name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"Saved jobs, newest first.","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","enum":["list"]},"url":{"type":"string"},"hasMore":{"type":"boolean"},"nextCursor":{"type":"string","nullable":true},"data":{"type":"array","items":{"$ref":"#/components/schemas/SavedJob"}}},"required":["object","url","hasMore","nextCursor","data"]}}}},"401":{"description":"Missing/invalid (`board_auth_invalid_token`) or expired (`board_auth_token_expired`) access token."},"403":{"description":"Token was issued for a different board."},"404":{"description":"Board not found."},"429":{"description":"Rate limited (`rate_limited`) — 100 requests per minute per board user."}},"tags":["Other"]},"post":{"security":[{"boardUserBearer":[]}],"summary":"Save a job","description":"Save a published job for the authenticated board user. Converges: saving an already-saved job returns the existing row unchanged, so retries are safe without an Idempotency-Key.","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":150,"description":"Board identifier, prefix-discriminated (ADR-0032): the board slug (mutable), a `boards_…` board ID (immutable), or a `pk_…` publishable key (immutable, revocable). Headless frontends should bind to `boards_…` or `pk_…` — slugs can be renamed by the operator."},"required":true,"description":"Board identifier, prefix-discriminated (ADR-0032): the board slug (mutable), a `boards_…` board ID (immutable), or a `pk_…` publishable key (immutable, revocable). Headless frontends should bind to `boards_…` or `pk_…` — slugs can be renamed by the operator.","name":"identifier","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SaveJobBody"}}}},"responses":{"200":{"description":"The saved job (newly saved or already saved — identical).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SavedJob"}}}},"401":{"description":"Missing/invalid (`board_auth_invalid_token`) or expired (`board_auth_token_expired`) access token."},"403":{"description":"Token was issued for a different board."},"404":{"description":"Board not found, or the job is unknown for this board (`candidate_job_not_found`) — malformed ID, another board’s job, or an unpublished job, indistinguishably."},"429":{"description":"Rate limited (`rate_limited`) — 100 requests per minute per board user."}},"tags":["Other"]}},"/boards/{identifier}/me/saved-jobs/{jobId}":{"delete":{"security":[{"boardUserBearer":[]}],"summary":"Unsave a job","description":"Remove a job from the authenticated board user’s saved list. Idempotent — always returns 204, including for unknown, malformed, or already-removed job IDs.","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":150,"description":"Board identifier, prefix-discriminated (ADR-0032): the board slug (mutable), a `boards_…` board ID (immutable), or a `pk_…` publishable key (immutable, revocable). Headless frontends should bind to `boards_…` or `pk_…` — slugs can be renamed by the operator."},"required":true,"description":"Board identifier, prefix-discriminated (ADR-0032): the board slug (mutable), a `boards_…` board ID (immutable), or a `pk_…` publishable key (immutable, revocable). Headless frontends should bind to `boards_…` or `pk_…` — slugs can be renamed by the operator.","name":"identifier","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":64},"required":true,"name":"jobId","in":"path"}],"responses":{"204":{"description":"Removed (or was never saved)."},"401":{"description":"Missing/invalid (`board_auth_invalid_token`) or expired (`board_auth_token_expired`) access token."},"403":{"description":"Token was issued for a different board."},"404":{"description":"Board not found."},"429":{"description":"Rate limited (`rate_limited`) — 100 requests per minute per board user."}},"tags":["Other"]}},"/companies/batch":{"post":{"summary":"Run a batch of company operations","description":"Runs up to 100 company operations in a single request, with a 5 MB request body limit. Each sub-operation runs independently and writes its own audit entry. Idempotency keys are honored on the batch itself, not on individual sub-operations.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompaniesBatchRequest"}}}},"responses":{"200":{"description":"Successful response. Inspect each entry of the `data` array for the per-sub-operation status.","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","enum":["batch_result"]},"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"number"},"data":{"nullable":true},"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"nullable":true}},"required":["code","message"]}},"required":["id","status"]}}},"required":["object","data"]}}}},"413":{"description":"The request body exceeded 5 MB."}},"tags":["Companies"]}},"/jobs/{id}/duplicate":{"post":{"summary":"Duplicate a job","description":"Creates a new draft job by copying the fields of an existing job. The slug of the new job is generated with a `copy-of-` prefix to keep it unique.","parameters":[{"schema":{"type":"string","minLength":1,"description":"The job's opaque object `id`: the `id` field returned by the job endpoints (e.g. `j8k2q7d4b9r3m6x0p5t1v7w2y9z4c8n3`). This is NOT the `slug` or title; passing a slug like `senior-backend-engineer` returns 404. To look up a job by title or slug, use `GET /v1/jobs` or `POST /v1/jobs/search` and read the returned `id`."},"required":true,"description":"The job's opaque object `id`: the `id` field returned by the job endpoints (e.g. `j8k2q7d4b9r3m6x0p5t1v7w2y9z4c8n3`). This is NOT the `slug` or title; passing a slug like `senior-backend-engineer` returns 404. To look up a job by title or slug, use `GET /v1/jobs` or `POST /v1/jobs/search` and read the returned `id`.","example":"j8k2q7d4b9r3m6x0p5t1v7w2y9z4c8n3","name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DuplicateJobBody"}}}},"responses":{"201":{"description":"Successful response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Job"}}}},"404":{"description":"Job not found."}},"tags":["Jobs"]}},"/jobs/{id}/expire":{"post":{"summary":"Expire a job","description":"Marks a published job as expired. The `status` is set to `expired` and `expiresAt` is set to the current time. Expired jobs are removed from the public board and can be reopened by publishing them again.","parameters":[{"schema":{"type":"string","minLength":1,"description":"The job's opaque object `id`: the `id` field returned by the job endpoints (e.g. `j8k2q7d4b9r3m6x0p5t1v7w2y9z4c8n3`). This is NOT the `slug` or title; passing a slug like `senior-backend-engineer` returns 404. To look up a job by title or slug, use `GET /v1/jobs` or `POST /v1/jobs/search` and read the returned `id`."},"required":true,"description":"The job's opaque object `id`: the `id` field returned by the job endpoints (e.g. `j8k2q7d4b9r3m6x0p5t1v7w2y9z4c8n3`). This is NOT the `slug` or title; passing a slug like `senior-backend-engineer` returns 404. To look up a job by title or slug, use `GET /v1/jobs` or `POST /v1/jobs/search` and read the returned `id`.","example":"j8k2q7d4b9r3m6x0p5t1v7w2y9z4c8n3","name":"id","in":"path"}],"responses":{"200":{"description":"Successful response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Job"}}}},"404":{"description":"Job not found."},"409":{"description":"The job is not currently published."}},"tags":["Jobs"]}},"/jobs/{id}/pause":{"post":{"summary":"Pause a job","description":"Returns a published job to draft status. The job is removed from the public board until it is published again.","parameters":[{"schema":{"type":"string","minLength":1,"description":"The job's opaque object `id`: the `id` field returned by the job endpoints (e.g. `j8k2q7d4b9r3m6x0p5t1v7w2y9z4c8n3`). This is NOT the `slug` or title; passing a slug like `senior-backend-engineer` returns 404. To look up a job by title or slug, use `GET /v1/jobs` or `POST /v1/jobs/search` and read the returned `id`."},"required":true,"description":"The job's opaque object `id`: the `id` field returned by the job endpoints (e.g. `j8k2q7d4b9r3m6x0p5t1v7w2y9z4c8n3`). This is NOT the `slug` or title; passing a slug like `senior-backend-engineer` returns 404. To look up a job by title or slug, use `GET /v1/jobs` or `POST /v1/jobs/search` and read the returned `id`.","example":"j8k2q7d4b9r3m6x0p5t1v7w2y9z4c8n3","name":"id","in":"path"}],"responses":{"200":{"description":"Successful response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Job"}}}},"404":{"description":"Job not found."},"409":{"description":"The job is not currently published."}},"tags":["Jobs"]}},"/jobs/{id}/publish":{"post":{"summary":"Publish a job","description":"Publishes a draft or expired job, making it visible on the public board. Plan and quota limits are enforced. `expiresAt` accepts an ISO 8601 string to set a new expiry, `null` to clear, or may be omitted. When omitted, a stored future expiry is preserved; a stored past expiry (e.g. set by a prior `expire` call) is cleared automatically so a republished job is never published with a stale past timestamp.","parameters":[{"schema":{"type":"string","minLength":1,"description":"The job's opaque object `id`: the `id` field returned by the job endpoints (e.g. `j8k2q7d4b9r3m6x0p5t1v7w2y9z4c8n3`). This is NOT the `slug` or title; passing a slug like `senior-backend-engineer` returns 404. To look up a job by title or slug, use `GET /v1/jobs` or `POST /v1/jobs/search` and read the returned `id`."},"required":true,"description":"The job's opaque object `id`: the `id` field returned by the job endpoints (e.g. `j8k2q7d4b9r3m6x0p5t1v7w2y9z4c8n3`). This is NOT the `slug` or title; passing a slug like `senior-backend-engineer` returns 404. To look up a job by title or slug, use `GET /v1/jobs` or `POST /v1/jobs/search` and read the returned `id`.","example":"j8k2q7d4b9r3m6x0p5t1v7w2y9z4c8n3","name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublishJobBody"}}}},"responses":{"200":{"description":"Successful response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Job"}}}},"404":{"description":"Job not found."},"409":{"description":"The job is already published, or the account's published-job quota is exhausted."}},"tags":["Jobs"]}},"/jobs/batch":{"post":{"summary":"Run a batch of job operations","description":"Runs up to 100 job operations in a single request. Supported sub-operations are create (`POST`), update (`PATCH`), delete (`DELETE`), publish, pause, expire, and duplicate. Each sub-operation runs independently; the response is HTTP 200 for any well-formed batch, and the per-operation outcome is reported on each entry of the `data` array.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchRequestBody"}}}},"responses":{"200":{"description":"Successful response. Inspect each entry of the `data` array for the per-sub-operation status.","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","enum":["batch_result"]},"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"number"},"data":{"nullable":true},"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"nullable":true}},"required":["code","message"]}},"required":["id","status"]}}},"required":["object","data"]}}}},"400":{"description":"The batch was malformed (too many sub-operations, duplicate sub-operation IDs, or invalid sub-operation shapes)."},"413":{"description":"The request body exceeded 5 MB."}},"tags":["Jobs"]}},"/jobs/{id}":{"get":{"summary":"Retrieve a job","description":"Retrieves the details of an existing job. You only need to supply the unique job ID that was returned upon job creation.","parameters":[{"schema":{"type":"string","minLength":1,"description":"The job's opaque object `id`: the `id` field returned by the job endpoints (e.g. `j8k2q7d4b9r3m6x0p5t1v7w2y9z4c8n3`). This is NOT the `slug` or title; passing a slug like `senior-backend-engineer` returns 404. To look up a job by title or slug, use `GET /v1/jobs` or `POST /v1/jobs/search` and read the returned `id`."},"required":true,"description":"The job's opaque object `id`: the `id` field returned by the job endpoints (e.g. `j8k2q7d4b9r3m6x0p5t1v7w2y9z4c8n3`). This is NOT the `slug` or title; passing a slug like `senior-backend-engineer` returns 404. To look up a job by title or slug, use `GET /v1/jobs` or `POST /v1/jobs/search` and read the returned `id`.","example":"j8k2q7d4b9r3m6x0p5t1v7w2y9z4c8n3","name":"id","in":"path"}],"responses":{"200":{"description":"Successful response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Job"}}}},"404":{"description":"Job not found."}},"tags":["Jobs"]},"patch":{"summary":"Update a job","description":"Updates the specified job by setting the values of the parameters passed. Any parameters not provided will be left unchanged. Pass `expiresAt: null` to clear the expiry.","parameters":[{"schema":{"type":"string","minLength":1,"description":"The job's opaque object `id`: the `id` field returned by the job endpoints (e.g. `j8k2q7d4b9r3m6x0p5t1v7w2y9z4c8n3`). This is NOT the `slug` or title; passing a slug like `senior-backend-engineer` returns 404. To look up a job by title or slug, use `GET /v1/jobs` or `POST /v1/jobs/search` and read the returned `id`."},"required":true,"description":"The job's opaque object `id`: the `id` field returned by the job endpoints (e.g. `j8k2q7d4b9r3m6x0p5t1v7w2y9z4c8n3`). This is NOT the `slug` or title; passing a slug like `senior-backend-engineer` returns 404. To look up a job by title or slug, use `GET /v1/jobs` or `POST /v1/jobs/search` and read the returned `id`.","example":"j8k2q7d4b9r3m6x0p5t1v7w2y9z4c8n3","name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateJobBody"}}}},"responses":{"200":{"description":"Successful response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Job"}}}},"400":{"description":"The request was malformed."},"404":{"description":"Job not found."}},"tags":["Jobs"]},"delete":{"summary":"Delete a job","description":"Permanently deletes a job. It cannot be undone. Saved jobs, orders, and any associated search-index entries are also removed.","parameters":[{"schema":{"type":"string","minLength":1,"description":"The job's opaque object `id`: the `id` field returned by the job endpoints (e.g. `j8k2q7d4b9r3m6x0p5t1v7w2y9z4c8n3`). This is NOT the `slug` or title; passing a slug like `senior-backend-engineer` returns 404. To look up a job by title or slug, use `GET /v1/jobs` or `POST /v1/jobs/search` and read the returned `id`."},"required":true,"description":"The job's opaque object `id`: the `id` field returned by the job endpoints (e.g. `j8k2q7d4b9r3m6x0p5t1v7w2y9z4c8n3`). This is NOT the `slug` or title; passing a slug like `senior-backend-engineer` returns 404. To look up a job by title or slug, use `GET /v1/jobs` or `POST /v1/jobs/search` and read the returned `id`.","example":"j8k2q7d4b9r3m6x0p5t1v7w2y9z4c8n3","name":"id","in":"path"}],"responses":{"204":{"description":"Successful response."},"404":{"description":"Job not found."}},"tags":["Jobs"]}},"/jobs/search":{"post":{"summary":"Search jobs","description":"Searches your jobs using a free-text query and faceted filters. Results are returned sorted by creation date, with the most recently created jobs appearing first. Multi-value filters match jobs in any of the supplied values; range filters accept `gte` and `lte` bounds. Each filter array accepts up to 10 values.","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchJobsBody"}}}},"responses":{"200":{"description":"Successful response.","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","enum":["search_result"]},"url":{"type":"string","example":"/v1/jobs/search"},"hasMore":{"type":"boolean"},"nextCursor":{"type":"string","nullable":true},"data":{"type":"array","items":{"$ref":"#/components/schemas/JobSummary"}}},"required":["object","url","hasMore","nextCursor","data"]}}}},"400":{"description":"The request was malformed, or a filter array exceeded the per-array cap."},"404":{"description":"A supplied `companyId` filter does not exist or is not accessible."}},"tags":["Jobs"]}},"/media/upload":{"post":{"summary":"Upload a media file","description":"Uploads a file via the unified media dispatcher. Send a multipart form with `file` (binary) and `purpose` (one of `board_logo`, `board_hero`, `account_avatar`, `company_logo`, or `blog_image`). Optionally include `resourceType` and `resourceId` to bind the upload to an owning resource. The request is idempotent when an `Idempotency-Key` header is supplied.","requestBody":{"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary"},"purpose":{"type":"string","enum":["board_logo","board_hero","account_avatar","company_logo","blog_image"]},"resourceType":{"type":"string"},"resourceId":{"type":"string"}},"required":["file","purpose"]}}}},"responses":{"201":{"description":"Successful response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MediaUpload"}}}},"400":{"description":"The `purpose` field is invalid, or the request was otherwise malformed."},"403":{"description":"The caller lacks the permission required for the requested upload purpose."},"404":{"description":"The owning resource for the upload (`resourceId`) does not exist."},"413":{"description":"The uploaded file exceeded the 5 MB size limit."},"415":{"description":"The uploaded file's MIME type is not supported."},"429":{"description":"Too many uploads. The limit is 30 per minute per account."}},"tags":["Media"],"x-internal":true}},"/media/{id}":{"get":{"summary":"Retrieve a media file","description":"Retrieves the metadata for a media file along with a fresh signed URL valid for fifteen minutes.","parameters":[{"schema":{"type":"string","minLength":1,"description":"The media file's opaque object `id`: the `id` returned by `POST /v1/media/upload` (a storage identifier, e.g. `s3t9v2x7z0b5d8f1h4k6m9p2r5t8w1y4`). This is an opaque token, not a filename, slug, or URL."},"required":true,"description":"The media file's opaque object `id`: the `id` returned by `POST /v1/media/upload` (a storage identifier, e.g. `s3t9v2x7z0b5d8f1h4k6m9p2r5t8w1y4`). This is an opaque token, not a filename, slug, or URL.","example":"s3t9v2x7z0b5d8f1h4k6m9p2r5t8w1y4","name":"id","in":"path"}],"responses":{"200":{"description":"Successful response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MediaGet"}}}},"404":{"description":"Media file not found."}},"tags":["Media"],"x-internal":true}},"/oauth/register":{"post":{"security":[],"summary":"Register an OAuth client","description":"Registers a new OAuth client following dynamic client registration. The plaintext `client_secret` is returned exactly once. Redirect URIs must use HTTPS, with the exception of `http://localhost` URIs in development.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DynamicClientRegistrationRequest"}}}},"responses":{"201":{"description":"Successful response.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/DynamicClientRegistrationResponse"}},"required":["data"]}}}}},"tags":["OAuth"],"x-internal":true}},"/usage":{"get":{"summary":"Retrieve account usage","description":"Retrieves the current usage and plan limits for your account, including the number of published jobs, the plan-enforced limit, the number of remaining slots, and the plan slug.","responses":{"200":{"description":"Successful response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Usage"}}}}},"tags":["Usage"]}},"/boards/{identifier}":{"get":{"security":[],"summary":"Retrieve public board context","description":"Returns the on-brand render payload a headless board frontend needs: identity, brand, capability flags, and theme tokens, through a public-safe allowlist. The `:identifier` is the board slug, `boards_…` ID, or `pk_…` publishable key (ADR-0032).","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":150,"description":"Board identifier, prefix-discriminated (ADR-0032): the board slug (mutable), a `boards_…` board ID (immutable), or a `pk_…` publishable key (immutable, revocable). Headless frontends should bind to `boards_…` or `pk_…` — slugs can be renamed by the operator."},"required":true,"description":"Board identifier, prefix-discriminated (ADR-0032): the board slug (mutable), a `boards_…` board ID (immutable), or a `pk_…` publishable key (immutable, revocable). Headless frontends should bind to `boards_…` or `pk_…` — slugs can be renamed by the operator.","name":"identifier","in":"path"}],"responses":{"200":{"description":"Successful response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicBoardContext"}}}},"404":{"description":"Public board not found (nonexistent or private board)."}},"tags":["Other"]}},"/boards/{identifier}/auth/register":{"post":{"security":[],"summary":"Board-user registration","description":"Create a board user (candidate or employer) with email + password and receive the bearer pair — registration signs the user in; email verification is non-blocking. A verification email (link + 6-digit code) is sent in the background; the link points at the board’s own URL (`/auth/verify-email?token=…` on its custom primary domain when one is set), so headless frontends on that domain should implement the route and feed the token to `POST /auth/verify-email`. An email that already belongs to a job-alert subscriber upgrades that subscriber to the requested role and clears any prior email verification.","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":150,"description":"Board identifier, prefix-discriminated (ADR-0032): the board slug (mutable), a `boards_…` board ID (immutable), or a `pk_…` publishable key (immutable, revocable). Headless frontends should bind to `boards_…` or `pk_…` — slugs can be renamed by the operator."},"required":true,"description":"Board identifier, prefix-discriminated (ADR-0032): the board slug (mutable), a `boards_…` board ID (immutable), or a `pk_…` publishable key (immutable, revocable). Headless frontends should bind to `boards_…` or `pk_…` — slugs can be renamed by the operator.","name":"identifier","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoardAuthRegisterBody"}}}},"responses":{"200":{"description":"Registered and signed in.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoardAuthSession"}}}},"403":{"description":"Sign-ups for the requested role are disabled on this board (`board_auth_registration_disabled`)."},"404":{"description":"Board not found (nonexistent or private)."},"409":{"description":"Email already registered (`board_auth_email_taken`). Nothing is mutated — the existing account is untouched."},"429":{"description":"Rate limited (`rate_limited`) — 10 requests per minute per IP, plus 5 sign-ups per 15 minutes per IP."}},"tags":["Other"]}},"/boards/{identifier}/auth/login":{"post":{"security":[],"summary":"Board-user login","description":"Authenticate a board user (candidate or employer) with email + password and receive a bearer pair: a 1-hour access JWT and a 30-day single-use refresh token. Throttled per IP and per (IP, email, board).","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":150,"description":"Board identifier, prefix-discriminated (ADR-0032): the board slug (mutable), a `boards_…` board ID (immutable), or a `pk_…` publishable key (immutable, revocable). Headless frontends should bind to `boards_…` or `pk_…` — slugs can be renamed by the operator."},"required":true,"description":"Board identifier, prefix-discriminated (ADR-0032): the board slug (mutable), a `boards_…` board ID (immutable), or a `pk_…` publishable key (immutable, revocable). Headless frontends should bind to `boards_…` or `pk_…` — slugs can be renamed by the operator.","name":"identifier","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoardAuthLoginBody"}}}},"responses":{"200":{"description":"Successful login.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoardAuthSession"}}}},"401":{"description":"Invalid credentials (`board_auth_invalid_credentials`)."},"404":{"description":"Board not found (nonexistent or private)."},"429":{"description":"Rate limited (`rate_limited`) — 10 requests per minute per IP, plus 5 attempts per 15 minutes per (IP, email, board)."}},"tags":["Other"]}},"/boards/{identifier}/auth/refresh":{"post":{"security":[],"summary":"Rotate the refresh token","description":"Exchange a live refresh token for a fresh bearer pair. Single-use: the presented token is consumed; reusing it returns 401 `board_auth_invalid_token`. An expired refresh token returns 401 `board_auth_token_expired` — re-login.","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":150,"description":"Board identifier, prefix-discriminated (ADR-0032): the board slug (mutable), a `boards_…` board ID (immutable), or a `pk_…` publishable key (immutable, revocable). Headless frontends should bind to `boards_…` or `pk_…` — slugs can be renamed by the operator."},"required":true,"description":"Board identifier, prefix-discriminated (ADR-0032): the board slug (mutable), a `boards_…` board ID (immutable), or a `pk_…` publishable key (immutable, revocable). Headless frontends should bind to `boards_…` or `pk_…` — slugs can be renamed by the operator.","name":"identifier","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoardAuthRefreshBody"}}}},"responses":{"200":{"description":"Rotated bearer pair.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoardAuthSession"}}}},"401":{"description":"Invalid (`board_auth_invalid_token`) or expired (`board_auth_token_expired`) refresh token."},"404":{"description":"Board not found."},"429":{"description":"Rate limited (`rate_limited`) — 10 requests per minute per IP."}},"tags":["Other"]}},"/boards/{identifier}/auth/logout":{"post":{"security":[],"summary":"Board-user logout","description":"Revoke a refresh token. Idempotent — always returns 204, including for unknown or malformed tokens. The access JWT remains valid until its 1-hour expiry; clients should discard it.","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":150,"description":"Board identifier, prefix-discriminated (ADR-0032): the board slug (mutable), a `boards_…` board ID (immutable), or a `pk_…` publishable key (immutable, revocable). Headless frontends should bind to `boards_…` or `pk_…` — slugs can be renamed by the operator."},"required":true,"description":"Board identifier, prefix-discriminated (ADR-0032): the board slug (mutable), a `boards_…` board ID (immutable), or a `pk_…` publishable key (immutable, revocable). Headless frontends should bind to `boards_…` or `pk_…` — slugs can be renamed by the operator.","name":"identifier","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoardAuthLogoutBody"}}}},"responses":{"204":{"description":"Refresh token revoked (or was already unusable)."},"404":{"description":"Board not found."},"429":{"description":"Rate limited (`rate_limited`) — 10 requests per minute per IP."}},"tags":["Other"]}},"/boards/{identifier}/auth/forgot-password":{"post":{"security":[],"summary":"Request a password reset email","description":"Request a password reset link for a board user. Always returns 204, whether or not the email is registered — no account enumeration. When the account exists, an email is sent in the background with a link to the board’s own `/auth/reset-password?token=…` page (custom primary domain honored — same headless-frontend contract as the verification email). The token is single-use and expires after 1 hour.","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":150,"description":"Board identifier, prefix-discriminated (ADR-0032): the board slug (mutable), a `boards_…` board ID (immutable), or a `pk_…` publishable key (immutable, revocable). Headless frontends should bind to `boards_…` or `pk_…` — slugs can be renamed by the operator."},"required":true,"description":"Board identifier, prefix-discriminated (ADR-0032): the board slug (mutable), a `boards_…` board ID (immutable), or a `pk_…` publishable key (immutable, revocable). Headless frontends should bind to `boards_…` or `pk_…` — slugs can be renamed by the operator.","name":"identifier","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoardAuthForgotPasswordBody"}}}},"responses":{"204":{"description":"Accepted (whether or not the email is registered)."},"404":{"description":"Board not found (nonexistent or private)."},"429":{"description":"Rate limited (`rate_limited`) — 10 requests per minute per IP, plus 3 requests per 15 minutes per (IP, email)."}},"tags":["Other"]}},"/boards/{identifier}/auth/reset-password":{"post":{"security":[],"summary":"Reset a password with a token","description":"Consume a password-reset token (1-hour TTL, single-use, delivered by email) and set a new password. On success every outstanding session dies: access JWTs and the refresh-token chain are invalidated, and the caller signs in again with the new password. Every rejection (unknown, expired, already-used, wrong-type, or cross-board token) is the single opaque 401 `board_auth_invalid_token`; request a fresh reset email to recover.","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":150,"description":"Board identifier, prefix-discriminated (ADR-0032): the board slug (mutable), a `boards_…` board ID (immutable), or a `pk_…` publishable key (immutable, revocable). Headless frontends should bind to `boards_…` or `pk_…` — slugs can be renamed by the operator."},"required":true,"description":"Board identifier, prefix-discriminated (ADR-0032): the board slug (mutable), a `boards_…` board ID (immutable), or a `pk_…` publishable key (immutable, revocable). Headless frontends should bind to `boards_…` or `pk_…` — slugs can be renamed by the operator.","name":"identifier","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoardAuthResetPasswordBody"}}}},"responses":{"204":{"description":"Password updated; all existing sessions revoked."},"401":{"description":"Invalid token (`board_auth_invalid_token`) — unknown, expired, already used, wrong-type, or issued for a different board."},"404":{"description":"Board not found (nonexistent or private)."},"429":{"description":"Rate limited (`rate_limited`) — 10 requests per minute per IP, plus 5 attempts per 15 minutes per IP."}},"tags":["Other"]}},"/boards/{identifier}/auth/verify-email":{"post":{"security":[],"summary":"Verify a board-user email","description":"Consume an email-verification token (24-hour TTL, delivered by email) and mark the board user’s email verified. Idempotent: a token that already verified its user keeps succeeding — replayed link clicks, and the link click after the user verified via the on-screen code, both return 204. Every rejection (unknown, expired, wrong-type, or cross-board token) is the single opaque 401 `board_auth_invalid_token`; request a fresh verification email to recover.","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":150,"description":"Board identifier, prefix-discriminated (ADR-0032): the board slug (mutable), a `boards_…` board ID (immutable), or a `pk_…` publishable key (immutable, revocable). Headless frontends should bind to `boards_…` or `pk_…` — slugs can be renamed by the operator."},"required":true,"description":"Board identifier, prefix-discriminated (ADR-0032): the board slug (mutable), a `boards_…` board ID (immutable), or a `pk_…` publishable key (immutable, revocable). Headless frontends should bind to `boards_…` or `pk_…` — slugs can be renamed by the operator.","name":"identifier","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoardAuthVerifyEmailBody"}}}},"responses":{"204":{"description":"Email verified (or was already verified)."},"401":{"description":"Invalid token (`board_auth_invalid_token`) — unknown, expired, wrong-type, or issued for a different board."},"404":{"description":"Board not found (nonexistent or private)."},"429":{"description":"Rate limited (`rate_limited`) — 10 requests per minute per IP."}},"tags":["Other"]}},"/boards/{identifier}/me":{"get":{"security":[{"boardUserBearer":[]}],"summary":"Retrieve the authenticated board user","description":"Session bootstrap for headless frontends. Requires a board-user access token; the token must belong to the board the `:identifier` resolves to (a token for another board returns 403).","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":150,"description":"Board identifier, prefix-discriminated (ADR-0032): the board slug (mutable), a `boards_…` board ID (immutable), or a `pk_…` publishable key (immutable, revocable). Headless frontends should bind to `boards_…` or `pk_…` — slugs can be renamed by the operator."},"required":true,"description":"Board identifier, prefix-discriminated (ADR-0032): the board slug (mutable), a `boards_…` board ID (immutable), or a `pk_…` publishable key (immutable, revocable). Headless frontends should bind to `boards_…` or `pk_…` — slugs can be renamed by the operator.","name":"identifier","in":"path"}],"responses":{"200":{"description":"The authenticated board user.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoardUser"}}}},"401":{"description":"Missing/invalid (`board_auth_invalid_token`) or expired (`board_auth_token_expired`) access token."},"403":{"description":"Token was issued for a different board."},"404":{"description":"Board not found."}},"tags":["Other"]}},"/blog/posts/{id}/publish":{"post":{"summary":"Publish a blog post","description":"Publishes a draft or scheduled post, making it visible on the public board. Takes no request body. `publishedAt` is stamped on the first publish and preserved on a later republish.","parameters":[{"schema":{"type":"string","minLength":1,"description":"The post's opaque object `id`: the `id` field returned by the blog-post endpoints (e.g. `b3p8m2k7d9q4x0r5t1v6w8y2z7a4c0n3`). This is NOT the `slug` or title; passing a slug like `my-first-post` returns 404. To look up a post by title or slug, use `GET /v1/blog/posts` or `POST /v1/blog/posts/search` and read the returned `id`."},"required":true,"description":"The post's opaque object `id`: the `id` field returned by the blog-post endpoints (e.g. `b3p8m2k7d9q4x0r5t1v6w8y2z7a4c0n3`). This is NOT the `slug` or title; passing a slug like `my-first-post` returns 404. To look up a post by title or slug, use `GET /v1/blog/posts` or `POST /v1/blog/posts/search` and read the returned `id`.","example":"b3p8m2k7d9q4x0r5t1v6w8y2z7a4c0n3","name":"id","in":"path"}],"responses":{"200":{"description":"Successful response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BlogPost"}}}},"404":{"description":"Blog post not found."},"409":{"description":"The post is already published."}},"tags":["Other"]}},"/blog/posts/{id}/unpublish":{"post":{"summary":"Unpublish a blog post","description":"Reverts a published post to a draft, removing it from the public board. Takes no request body. The stored `publishedAt` is preserved so a later republish keeps the original publish date.","parameters":[{"schema":{"type":"string","minLength":1,"description":"The post's opaque object `id`: the `id` field returned by the blog-post endpoints (e.g. `b3p8m2k7d9q4x0r5t1v6w8y2z7a4c0n3`). This is NOT the `slug` or title; passing a slug like `my-first-post` returns 404. To look up a post by title or slug, use `GET /v1/blog/posts` or `POST /v1/blog/posts/search` and read the returned `id`."},"required":true,"description":"The post's opaque object `id`: the `id` field returned by the blog-post endpoints (e.g. `b3p8m2k7d9q4x0r5t1v6w8y2z7a4c0n3`). This is NOT the `slug` or title; passing a slug like `my-first-post` returns 404. To look up a post by title or slug, use `GET /v1/blog/posts` or `POST /v1/blog/posts/search` and read the returned `id`.","example":"b3p8m2k7d9q4x0r5t1v6w8y2z7a4c0n3","name":"id","in":"path"}],"responses":{"200":{"description":"Successful response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BlogPost"}}}},"404":{"description":"Blog post not found."},"409":{"description":"The post is not currently published."}},"tags":["Other"]}},"/blog/posts/{id}/toggle-featured":{"post":{"summary":"Toggle a blog post's featured flag","description":"Flips the post's `featured` flag and returns the updated post. Takes no request body.","parameters":[{"schema":{"type":"string","minLength":1,"description":"The post's opaque object `id`: the `id` field returned by the blog-post endpoints (e.g. `b3p8m2k7d9q4x0r5t1v6w8y2z7a4c0n3`). This is NOT the `slug` or title; passing a slug like `my-first-post` returns 404. To look up a post by title or slug, use `GET /v1/blog/posts` or `POST /v1/blog/posts/search` and read the returned `id`."},"required":true,"description":"The post's opaque object `id`: the `id` field returned by the blog-post endpoints (e.g. `b3p8m2k7d9q4x0r5t1v6w8y2z7a4c0n3`). This is NOT the `slug` or title; passing a slug like `my-first-post` returns 404. To look up a post by title or slug, use `GET /v1/blog/posts` or `POST /v1/blog/posts/search` and read the returned `id`.","example":"b3p8m2k7d9q4x0r5t1v6w8y2z7a4c0n3","name":"id","in":"path"}],"responses":{"200":{"description":"Successful response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BlogPost"}}}},"404":{"description":"Blog post not found."}},"tags":["Other"]}},"/oauth/authorize":{"get":{"security":[],"summary":"Start an OAuth authorization flow","description":"Validates an OAuth authorization request, ensures the user is signed in, and redirects to the consent page. PKCE is required and must use the `S256` challenge method; the `plain` method is rejected.","parameters":[{"schema":{"type":"string","enum":["code"],"description":"Must be `code`. The implicit and hybrid flows are not supported."},"required":true,"description":"Must be `code`. The implicit and hybrid flows are not supported.","name":"response_type","in":"query"},{"schema":{"type":"string","minLength":1,"description":"Identifier issued for the OAuth client at registration."},"required":true,"description":"Identifier issued for the OAuth client at registration.","name":"client_id","in":"query"},{"schema":{"type":"string","minLength":1,"description":"Where the user is redirected after consent. Must exactly match a redirect URI registered with the client."},"required":true,"description":"Where the user is redirected after consent. Must exactly match a redirect URI registered with the client.","name":"redirect_uri","in":"query"},{"schema":{"type":"string","minLength":1,"description":"PKCE code challenge derived from the code verifier (RFC 7636)."},"required":true,"description":"PKCE code challenge derived from the code verifier (RFC 7636).","name":"code_challenge","in":"query"},{"schema":{"type":"string","enum":["S256"],"description":"Must be `S256`. The `plain` method is rejected."},"required":true,"description":"Must be `S256`. The `plain` method is rejected.","name":"code_challenge_method","in":"query"},{"schema":{"type":"string","description":"Space-separated list of requested scopes."},"required":false,"description":"Space-separated list of requested scopes.","name":"scope","in":"query"},{"schema":{"type":"string","description":"Opaque value echoed back to the redirect URI. Use it to maintain state across the redirect and to mitigate CSRF."},"required":false,"description":"Opaque value echoed back to the redirect URI. Use it to maintain state across the redirect and to mitigate CSRF.","name":"state","in":"query"}],"responses":{"302":{"description":"Redirect — to the consent page on success, to sign-in if the user is unauthenticated, or to the client's `redirect_uri` if the request was rejected."}},"tags":["OAuth"],"x-internal":true},"post":{"security":[],"summary":"Submit an OAuth consent decision","description":"Receives the consent form submission from the authorization page. On approval, an authorization code is issued and the user is redirected to the client's `redirect_uri`. On denial, the user is redirected to `redirect_uri` with `error=access_denied`.","responses":{"302":{"description":"Redirect to the client's `redirect_uri`, with `code` and `state` on approval, or with `error=access_denied` on denial."}},"tags":["OAuth"],"x-internal":true}},"/oauth/token":{"post":{"security":[],"summary":"Exchange an authorization code or refresh token","description":"Issues new access and refresh tokens. Supports the `authorization_code` and `refresh_token` grant types. PKCE is required for `authorization_code`. Refresh tokens are single-use; reusing a refresh token revokes the entire token chain.","requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/TokenRequest"}}}},"responses":{"200":{"description":"Successful response.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/TokenResponse"}},"required":["data"]}}}}},"tags":["OAuth"],"x-internal":true}},"/oauth/revoke":{"post":{"security":[],"summary":"Revoke an OAuth token","description":"Revokes an access or refresh token. Always returns 200, even for unknown tokens, to avoid revealing whether a given token exists.","requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/RevokeRequest"}}}},"responses":{"200":{"description":"Successful response. Always returned, even for tokens that did not exist.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"revoked":{"type":"boolean"}},"required":["revoked"]}},"required":["data"]}}}}},"tags":["OAuth"],"x-internal":true}},"/operations":{"get":{"summary":"List all operations","description":"Returns a list of asynchronous operations for your account. The operations are returned sorted by creation date, with the most recently created operations appearing first.","parameters":[{"schema":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Only return operations in the given state. Repeatable: pass once per state to OR-combine values."},"required":false,"description":"Only return operations in the given state. Repeatable: pass once per state to OR-combine values.","name":"state","in":"query"},{"schema":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Only return operations of the given kind. Repeatable: pass once per kind to OR-combine values."},"required":false,"description":"Only return operations of the given kind. Repeatable: pass once per kind to OR-combine values.","name":"kind","in":"query"},{"schema":{"type":"string","description":"Only return operations on the given resource type."},"required":false,"description":"Only return operations on the given resource type.","name":"resourceType","in":"query"},{"schema":{"type":"string","description":"Only return operations on the given resource ID."},"required":false,"description":"Only return operations on the given resource ID.","name":"resourceId","in":"query"},{"schema":{"type":"string","format":"date-time","description":"Only return operations with a `createdAt` at or after this ISO 8601 datetime."},"required":false,"description":"Only return operations with a `createdAt` at or after this ISO 8601 datetime.","name":"from","in":"query"},{"schema":{"type":"string","format":"date-time","description":"Only return operations with a `createdAt` at or before this ISO 8601 datetime."},"required":false,"description":"Only return operations with a `createdAt` at or before this ISO 8601 datetime.","name":"to","in":"query"},{"schema":{"type":"string","minLength":1,"description":"An opaque pagination cursor returned in the `nextCursor` field of a previous response. Pass it back to fetch the next page of results."},"required":false,"description":"An opaque pagination cursor returned in the `nextCursor` field of a previous response. Pass it back to fetch the next page of results.","name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50,"description":"A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 50."},"required":false,"description":"A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 50.","name":"limit","in":"query"}],"responses":{"200":{"description":"Successful response.","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","enum":["list"]},"url":{"type":"string","example":"/v1/operations"},"hasMore":{"type":"boolean"},"nextCursor":{"type":"string","nullable":true},"data":{"type":"array","items":{"$ref":"#/components/schemas/OperationResource"}}},"required":["object","url","hasMore","nextCursor","data"]}}}}},"tags":["Operations"],"x-internal":true}},"/operations/{id}":{"get":{"summary":"Retrieve an operation","description":"Retrieves the details of an existing operation. Operations are garbage-collected thirty days after reaching a terminal state.","parameters":[{"schema":{"type":"string","minLength":1,"description":"Unique identifier for the operation."},"required":true,"description":"Unique identifier for the operation.","name":"id","in":"path"}],"responses":{"200":{"description":"Successful response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperationResource"}}}}},"tags":["Operations"],"x-internal":true}},"/operations/{id}/children":{"get":{"summary":"List child operations","description":"Returns a list of child operations for a parent operation that fans out per item, such as a bulk enrichment that spawns one operation per company. Returns an empty list for operation kinds that do not fan out.","parameters":[{"schema":{"type":"string","minLength":1,"description":"Unique identifier for the operation."},"required":true,"description":"Unique identifier for the operation.","name":"id","in":"path"},{"schema":{"type":"string","minLength":1,"description":"An opaque pagination cursor returned in the `nextCursor` field of a previous response. Pass it back to fetch the next page of results."},"required":false,"description":"An opaque pagination cursor returned in the `nextCursor` field of a previous response. Pass it back to fetch the next page of results.","name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50,"description":"A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 50."},"required":false,"description":"A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 50.","name":"limit","in":"query"}],"responses":{"200":{"description":"Successful response.","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","enum":["list"]},"url":{"type":"string","example":"/v1/operations"},"hasMore":{"type":"boolean"},"nextCursor":{"type":"string","nullable":true},"data":{"type":"array","items":{"$ref":"#/components/schemas/OperationResource"}}},"required":["object","url","hasMore","nextCursor","data"]}}}}},"tags":["Operations"],"x-internal":true}},"/operations/{id}/cancel":{"post":{"summary":"Cancel an operation","description":"Requests cancellation of an operation. A pending operation is cancelled immediately. A running operation is flagged for cancellation; the worker observes the flag at its next checkpoint. Operations that have already reached a terminal state cannot be cancelled. Cancellation does not roll back work that has already completed.","parameters":[{"schema":{"type":"string","minLength":1,"description":"Unique identifier for the operation."},"required":true,"description":"Unique identifier for the operation.","name":"id","in":"path"}],"responses":{"200":{"description":"Successful response. The returned operation reflects `cancelRequested: true`; the worker observes the flag at its next checkpoint.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperationResource"}}}}},"tags":["Operations"],"x-internal":true}},"/settings":{"get":{"summary":"Retrieve board settings","description":"Retrieves the settings for your board.","responses":{"200":{"description":"Successful response."}},"tags":["Settings"],"x-internal":true},"patch":{"summary":"Update board settings","description":"Updates the board settings by setting the values of the parameters passed. Any parameters not provided will be left unchanged. Reserved fields such as `passwordProtectionEnabled`, `adsenseClientId`, and `jobAccessPaywallEnabled` must be set via their dedicated endpoints; sending them through the free-form `config` object is rejected.","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchSettingsBody"}}}},"responses":{"200":{"description":"Successful response."},"400":{"description":"The request was malformed."},"403":{"description":"The account's plan tier does not permit this change. Common cases include disabling password protection on a plan that requires it, and removing Cavuno branding on a plan that does not allow it."},"409":{"description":"The supplied slug is already in use, or password protection cannot be enabled until a password is set via `POST /v1/settings/password-protection`."}},"tags":["Settings"],"x-internal":true}},"/settings/adsense":{"get":{"summary":"Retrieve AdSense configuration","description":"Retrieves the AdSense configuration for your board.","responses":{"200":{"description":"Successful response."}},"tags":["Settings"],"x-internal":true},"put":{"summary":"Update AdSense configuration","description":"Replaces the AdSense configuration for your board with the supplied values.","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SettingsAdsenseBody"}}}},"responses":{"200":{"description":"Successful response."},"400":{"description":"The supplied AdSense client ID is not in the required `ca-pub-XXXXXXXXXXXXXXXX` format."}},"tags":["Settings"],"x-internal":true}},"/settings/paywall":{"get":{"summary":"Retrieve paywall configuration","description":"Retrieves the paywall configuration for your board.","responses":{"200":{"description":"Successful response."}},"tags":["Settings"],"x-internal":true},"put":{"summary":"Update paywall configuration","description":"Replaces the paywall configuration for your board with the supplied values.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SettingsPaywallBody"}}}},"responses":{"200":{"description":"Successful response."}},"tags":["Settings"],"x-internal":true}},"/settings/hero":{"delete":{"summary":"Delete the board hero image","description":"Removes the hero image from your board.","responses":{"204":{"description":"Successful response."},"409":{"description":"No hero image is currently set."}},"tags":["Settings"],"x-internal":true}},"/settings/password-protection":{"post":{"summary":"Enable password protection","description":"Enables password protection for your board with the supplied password. The password must be at least eight characters.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SettingsPasswordProtectionBody"}}}},"responses":{"200":{"description":"Successful response."},"400":{"description":"The password is shorter than the 8-character minimum."}},"tags":["Settings"],"x-internal":true},"delete":{"summary":"Disable password protection","description":"Disables password protection on your board. Free-plan accounts cannot disable password protection and will receive a 403.","responses":{"204":{"description":"Successful response."},"403":{"description":"The account's plan tier does not permit a public board; password protection cannot be disabled."},"409":{"description":"Password protection is not currently enabled."}},"tags":["Settings"],"x-internal":true}},"/settings/job-form":{"get":{"summary":"Retrieve the job form configuration","description":"Retrieves the board's job form configuration — built-in field tuning plus any custom field definitions. Read this before writing custom-field values: each definition's `key`, `type`, and `options` tell you what to send in the `customFieldValues` object on `POST`/`PATCH /v1/jobs`. Returns an empty object when the board has not configured it.","responses":{"200":{"description":"Successful response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobFormConfig"}}}}},"tags":["Settings"],"x-internal":true}},"/taxonomies/remote-permits":{"get":{"summary":"List remote-permit taxonomy","tags":["Taxonomies"],"description":"Returns the canonical `{type, value, label}` set accepted by the `remotePermits` field on `POST /v1/jobs` and `PATCH /v1/jobs/:id`. Public — no authentication required. Cached for 6h.","responses":{"200":{"description":"Successful response.","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","enum":["list"]},"url":{"type":"string","example":"/v1/taxonomies/remote-permits"},"hasMore":{"type":"boolean"},"nextCursor":{"type":"string","nullable":true},"data":{"type":"array","items":{"$ref":"#/components/schemas/RemotePermitTaxonomyEntry"}}},"required":["object","url","hasMore","nextCursor","data"]}}}}}}},"/taxonomies/remote-timezones":{"get":{"summary":"List remote-timezone taxonomy","tags":["Taxonomies"],"description":"Returns the canonical `{type, value, label}` set accepted by the `remoteTimezones` field on `POST /v1/jobs` and `PATCH /v1/jobs/:id`. Public — no authentication required. Cached for 6h.","responses":{"200":{"description":"Successful response.","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","enum":["list"]},"url":{"type":"string","example":"/v1/taxonomies/remote-timezones"},"hasMore":{"type":"boolean"},"nextCursor":{"type":"string","nullable":true},"data":{"type":"array","items":{"$ref":"#/components/schemas/RemoteTimezoneTaxonomyEntry"}}},"required":["object","url","hasMore","nextCursor","data"]}}}}}}}}}