Every API here is available over the APIs.io API and to AI agents over MCP.
openapi: 3.2.0
info:
title: Cavuno v1 REST Job feeds 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. ## Base URL
Production: `https://api.cavuno.com/v1`
All API requests must be made over HTTPS. Calls made over plain HTTP will fail. ## Authentication
The Cavuno API uses API keys to authenticate requests. You can view and manage your API keys in the Cavuno dashboard, under Settings → Developer → API keys. Cavuno 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. Authentication to the API is performed via Bearer auth. Provide your API key as the bearer token in the `Authorization` header:
```bash
curl https://api.cavuno.com/v1/jobs \
-H "Authorization: Bearer YOUR_SECRET_API_KEY"
```
API requests without authentication will fail. ## Rate limits
The 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. If you have a use case that needs a higher limit, contact support. ## Idempotency
The 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. To perform an idempotent request, provide an additional `Idempotency-Key` header on any `POST`, `PATCH`, or `DELETE` request:
```bash
curl https://api.cavuno.com/v1/jobs \
-X POST \
-H "Authorization: Bearer cavuno_live_..." \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d ''{ "title": "Senior Engineer" }''
```
## Errors
Cavuno 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). Error responses include a JSON body with details:
```json
{
"error": {
"code": "validation_bad_request",
"message": "Field ''title'' is required.",
"requestId": "req_..."
}
}
```
Some errors carry an additional `details` field with structured context, such as per-field validation issues. ### Plan upgrade hints
When an Operator API request is refused because of the board''s Cavuno plan, the error also carries an `upgrade` object. It names the capability that blocked the request, the cheapest self-serve plan that unlocks it (when that can be told), and the hosted billing endpoints that start the upgrade:
```json
{
"error": {
"code": "settings_plan_required_for_branding_removal",
"message": "Removing Cavuno branding requires a plan that includes branding removal.",
"requestId": "req_...",
"upgrade": {
"capability": "canRemoveBranding",
"requiredPlan": "basic",
"checkout": { "method": "POST", "path": "/v1/billing/checkout" },
"changePlan": { "method": "POST", "path": "/v1/billing/upgrade" }
}
}
}
```
Call `GET /v1/billing/subscription` first. With no active subscription, use `checkout`; with one, use `changePlan`. Send `{ "planKey": "" }` and give the returned `url` to a person: both endpoints return a hosted page where a person confirms the purchase. Nothing is charged until they do. Each `path` is relative to the API origin (`https://api.cavuno.com`), like `Location` headers and list `url` fields, so do not join it to a base URL that already ends in `/v1`.'
contact:
name: Cavuno
url: https://cavuno.com
servers:
- url: https://api.cavuno.com/v1
description: Production
security:
- bearerAuth: []
tags:
- name: Job feeds
description: 'Recurring XML job feeds pulled from a URL on a schedule: connect, map fields, re-detect, and track runs.'
paths:
/job-feeds:
get:
summary: List job feeds
description: Returns your Board's recurring job feeds, oldest first.
parameters:
- schema:
type: string
minLength: 1
required: false
name: cursor
in: query
- schema:
type: integer
minimum: 1
maximum: 50
required: false
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/job-feeds
hasMore:
type: boolean
nextCursor:
type:
- string
- 'null'
data:
type: array
items:
$ref: '#/components/schemas/JobFeed'
required:
- object
- url
- hasMore
- nextCursor
- data
'400':
description: The pagination cursor or query is invalid.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
x-audience: operator
tags:
- Job feeds
operationId: listJobFeeds
post:
summary: Create a job feed
description: Connects an XML job feed URL (for example an ATS or Indeed-style feed). The URL is fetched once to check it serves XML before it is saved. The first run starts straight away and detects the field mapping; until it finishes the feed is `pending_mapping` and `mapping` is `null`. Poll `GET /job-feeds/{id}`.
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateJobFeedBody'
responses:
'201':
description: The created job feed.
content:
application/json:
schema:
$ref: '#/components/schemas/JobFeedDetail'
'400':
description: Invalid request.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'409':
description: The Board already has its maximum job feeds.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'422':
description: The feed URL failed the pre-save check.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'429':
description: Too many feed URL checks.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
x-audience: operator
tags:
- Job feeds
operationId: createJobFeed
/job-feeds/{id}:
get:
summary: Retrieve a job feed
description: Returns one job feed with its schedule, last run outcome, and mapping review state.
parameters:
- schema:
type: string
minLength: 1
description: The job feed ID.
required: true
description: The job feed ID.
name: id
in: path
responses:
'200':
description: Successful response.
content:
application/json:
schema:
$ref: '#/components/schemas/JobFeedDetail'
'404':
description: Job feed not found.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
x-audience: operator
tags:
- Job feeds
operationId: getJobFeed
patch:
summary: Update a job feed
description: Changes any of the URL, name, schedule, enabled state, job limit, apply-URL attribution, and mapping in one request. A changed URL is checked first. A mapping edit applies from the next run, which starts now.
parameters:
- schema:
type: string
minLength: 1
description: The job feed ID.
required: true
description: The job feed ID.
name: id
in: path
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/UpdateJobFeedBody'
responses:
'200':
description: The updated job feed.
content:
application/json:
schema:
$ref: '#/components/schemas/JobFeedDetail'
'400':
description: Invalid request.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'404':
description: Job feed not found.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'409':
description: A mapping edit before the feed has a mapping.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'422':
description: The new feed URL failed the pre-save check.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'429':
description: Too many feed URL checks.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
x-audience: operator
tags:
- Job feeds
operationId: updateJobFeed
delete:
summary: Delete a job feed
description: Stops pulling the feed. Jobs it already published stay published and are no longer linked to it.
parameters:
- schema:
type: string
minLength: 1
description: The job feed ID.
required: true
description: The job feed ID.
name: id
in: path
responses:
'204':
description: Successful response.
'404':
description: Job feed not found.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
x-audience: operator
tags:
- Job feeds
operationId: deleteJobFeed
/job-feeds/{id}/redetect-mapping:
post:
summary: Re-detect a job feed mapping
description: 'Discards the current mapping and detects a fresh one on the next run, which starts now. The feed is returned with `mapping: null` until that run finishes.'
parameters:
- schema:
type: string
minLength: 1
description: The job feed ID.
required: true
description: The job feed ID.
name: id
in: path
responses:
'200':
description: The job feed.
content:
application/json:
schema:
$ref: '#/components/schemas/JobFeedDetail'
'404':
description: Job feed not found.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
x-audience: operator
tags:
- Job feeds
operationId: redetectJobFeedMapping
/job-feeds/{id}/runs:
get:
summary: List job feed runs
description: Returns the runs of one feed, most recent first, with row counts and row errors.
parameters:
- schema:
type: string
minLength: 1
description: The job feed ID.
required: true
description: The job feed ID.
name: id
in: path
- schema:
type: string
minLength: 1
required: false
name: cursor
in: query
- schema:
type: integer
minimum: 1
maximum: 50
required: false
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/job-feeds/{id}/runs
hasMore:
type: boolean
nextCursor:
type:
- string
- 'null'
data:
type: array
items:
$ref: '#/components/schemas/JobFeedRun'
required:
- object
- url
- hasMore
- nextCursor
- data
'400':
description: The pagination cursor or query is invalid.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'404':
description: Job feed not found.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
x-audience: operator
tags:
- Job feeds
operationId: listJobFeedRuns
components:
schemas:
JobFeedDetail:
type: object
properties:
id:
type: string
object:
type: string
enum:
- job_feed
name:
type: string
url:
type: string
status:
type: string
enum:
- pending_mapping
- active
- failing
- needs_review
description: '`pending_mapping`: the first run (or a re-detection) has not produced a mapping yet. `failing`: recent runs failed (see `lastError`). `needs_review`: runs are held until the mapping is fixed or re-detected.'
enabled:
type: boolean
intervalHours:
type: number
expireMissing:
type: boolean
jobLimit:
type:
- number
- 'null'
applyUrlAttribution:
$ref: '#/components/schemas/JobFeedApplyUrlAttribution'
lastRunAt:
type:
- string
- 'null'
nextRunAt:
type:
- string
- 'null'
description: When the next pull is due. `null` while the feed is disabled.
lastError:
type:
- string
- 'null'
consecutiveFailures:
type: number
createdAt:
type: string
mapping:
type:
- object
- 'null'
properties:
fieldMappings:
type: array
items:
type: object
properties:
sourceColumn:
type: string
targetField:
type: string
confidence:
type: number
notes:
type: string
required:
- sourceColumn
- targetField
- confidence
valueTransforms:
type: array
items:
type: object
properties:
sourceColumn:
type: string
targetField:
type: string
map:
type: object
additionalProperties:
type: string
fallback:
type: string
required:
- sourceColumn
- targetField
- map
defaults:
type: object
properties:
employmentType:
type: string
remoteOption:
type: string
currency:
type: string
descriptionFormat:
type: string
fieldsNeedingReview:
type: array
items:
type: string
description: Job fields whose detected mapping should be checked.
sourceColumns:
type: array
items:
type: object
properties:
name:
type: string
samples:
type: array
items:
type: string
required:
- name
- samples
description: Columns seen in the feed, with a few sample values.
required:
- fieldMappings
- valueTransforms
- defaults
- fieldsNeedingReview
- sourceColumns
description: Typed, operator-reviewable mapping. `null` until the first run (or a re-detection) has detected one.
required:
- id
- object
- name
- url
- status
- enabled
- intervalHours
- expireMissing
- jobLimit
- applyUrlAttribution
- lastRunAt
- nextRunAt
- lastError
- consecutiveFailures
- createdAt
- mapping
ApplyUrlParamTemplate:
type: object
properties:
key:
type: string
minLength: 1
maxLength: 64
pattern: ^[A-Za-z0-9][A-Za-z0-9_.:-]*$
valueTemplate:
type: string
maxLength: 200
required:
- key
- valueTemplate
additionalProperties: false
JobFeedFieldMapping:
type: object
properties:
sourceColumn:
type: string
minLength: 1
maxLength: 500
targetField:
type: string
description: A Cavuno job field, e.g. `title`, `company_name`, `description`, `apply_url`, `location_label`, `employment_type`.
confidence:
type: number
minimum: 0
maximum: 1
notes:
type: string
maxLength: 1000
required:
- sourceColumn
- targetField
- confidence
additionalProperties: false
CreateJobFeedBody:
type: object
properties:
url:
type: string
minLength: 1
maxLength: 2048
format: uri
description: 'The XML feed URL. Checked before it is saved: Cavuno fetches it once and refuses a blocked, unreachable or non-XML URL.'
name:
type: string
minLength: 1
maxLength: 80
description: Display name. Defaults to a name derived from the URL host.
intervalHours:
anyOf:
- type: number
enum:
- 1
- type: number
enum:
- 3
- type: number
enum:
- 6
- type: number
enum:
- 12
- type: number
enum:
- 24
default: 6
description: Hours between pulls.
expireMissing:
type: boolean
default: true
description: Expire a job once it is missing from two successful runs in a row.
jobLimit:
type:
- integer
- 'null'
minimum: 1
maximum: 100000
default: null
description: Most jobs this feed may have published at once. `null` means no per-feed cap (plan and Board limits still apply).
required:
- url
additionalProperties: false
UpdateJobFeedBody:
type: object
properties:
url:
type: string
minLength: 1
maxLength: 2048
format: uri
description: 'The XML feed URL. Checked before it is saved: Cavuno fetches it once and refuses a blocked, unreachable or non-XML URL.'
name:
type: string
minLength: 1
maxLength: 80
intervalHours:
anyOf:
- type: number
enum:
- 1
- type: number
enum:
- 3
- type: number
enum:
- 6
- type: number
enum:
- 12
- type: number
enum:
- 24
description: Hours between pulls.
expireMissing:
type: boolean
enabled:
type: boolean
description: A disabled feed is not pulled until it is enabled again.
jobLimit:
type:
- integer
- 'null'
minimum: 1
maximum: 100000
description: Most jobs this feed may have published at once. `null` means no per-feed cap (plan and Board limits still apply).
applyUrlAttribution:
$ref: '#/components/schemas/JobFeedApplyUrlAttribution'
mapping:
$ref: '#/components/schemas/JobFeedMappingInput'
additionalProperties: false
JobFeedRun:
type: object
properties:
id:
type: string
object:
type: string
enum:
- job_feed_run
status:
type: string
totalRows:
type: number
processedRows:
type: number
createdCount:
type: number
updatedCount:
type: number
skippedCount:
type: number
errorCount:
type: number
errors:
type: array
items:
type: object
properties:
rowIndex:
type: number
code:
type: string
message:
type: string
required:
- rowIndex
- code
- message
description: The first 20 row errors; `errorCount` is the total.
startedAt:
type:
- string
- 'null'
completedAt:
type:
- string
- 'null'
createdAt:
type: string
required:
- id
- object
- status
- totalRows
- processedRows
- createdCount
- updatedCount
- skippedCount
- errorCount
- errors
- startedAt
- completedAt
- createdAt
JobFeedValueTransform:
type: object
properties:
sourceColumn:
type: string
minLength: 1
maxLength: 500
targetField:
type: string
description: A Cavuno job field, e.g. `title`, `company_name`, `description`, `apply_url`, `location_label`, `employment_type`.
map:
type: object
additionalProperties:
type: string
description: Source value → Cavuno value.
fallback:
type: string
required:
- sourceColumn
- targetField
- map
additionalProperties: false
JobFeedMappingInput:
type: object
properties:
fieldMappings:
type: array
items:
$ref: '#/components/schemas/JobFeedFieldMapping'
maxItems: 200
valueTransforms:
type: array
items:
$ref: '#/components/schemas/JobFeedValueTransform'
maxItems: 200
default: []
required:
- fieldMappings
additionalProperties: false
description: Replaces the feed's column mappings and value transforms. Applies from the next run, which starts now.
JobFeedApplyUrlAttribution:
oneOf:
- type: object
properties:
mode:
type: string
enum:
- inherit
required:
- mode
additionalProperties: false
- type: object
properties:
mode:
type: string
enum:
- 'off'
required:
- mode
additionalProperties: false
- type: object
properties:
mode:
type: string
enum:
- append
params:
type: array
items:
$ref: '#/components/schemas/ApplyUrlParamTemplate'
maxItems: 8
required:
- mode
- params
additionalProperties: false
description: Outbound apply-URL attribution for this feed's jobs. `inherit` uses the Board setting.
PlanUpgradeHint:
type: object
properties:
capability:
type: string
enum:
- activeJobsLimit
- teamSeatLimit
- canBePublic
- canRemoveBranding
- apiAccess
description: The plan capability that blocked the request, named as in `GET /v1/billing/subscription` `capabilities`.
requiredPlan:
type: string
enum:
- starter
- basic
- grow
- advanced
- scale
description: The cheapest self-serve plan that unlocks the action. Pass it as `planKey`. Omitted when it cannot be told without more information; read `GET /v1/billing/subscription` and pick a higher plan.
checkout:
type: object
properties:
method:
type: string
enum:
- POST
path:
type: string
enum:
- /v1/billing/checkout
required:
- method
- path
description: Starts a hosted checkout when the board has no active subscription (for example during a trial).
changePlan:
type: object
properties:
method:
type: string
enum:
- POST
path:
type: string
enum:
- /v1/billing/upgrade
required:
- method
- path
description: Starts a hosted plan change when the board already has an active subscription.
required:
- capability
- checkout
- changePlan
description: 'Present only on Operator API errors caused by the board''s Cavuno plan. Read `GET /v1/billing/subscription` to choose between `checkout` and `changePlan`, call it with `{ "planKey": requiredPlan }`, and give the returned `url` to a person to confirm. Each `path` is relative to the API origin, not to a `/v1` base URL. Both endpoints need `billing.manage`. Board API errors never include it.'
JobFeed:
type: object
properties:
id:
type: string
object:
type: string
enum:
- job_feed
name:
type: string
url:
type: string
status:
type: string
enum:
- pending_mapping
- active
- failing
- needs_review
description: '`pending_mapping`: the first run (or a re-detection) has not produced a mapping yet. `failing`: recent runs failed (see `lastError`). `needs_review`: runs are held until the mapping is fixed or re-detected.'
enabled:
type: boolean
intervalHours:
type: number
expireMissing:
type: boolean
jobLimit:
type:
- number
- 'null'
applyUrlAttribution:
$ref: '#/components/schemas/JobFeedApplyUrlAttribution'
lastRunAt:
type:
- string
- 'null'
nextRunAt:
type:
- string
- 'null'
description: When the next pull is due. `null` while the feed is disabled.
lastError:
type:
- string
- 'null'
consecutiveFailures:
type: number
createdAt:
type: string
required:
- id
- object
- name
- url
- status
- enabled
- intervalHours
- expireMissing
- jobLimit
- applyUrlAttribution
- lastRunAt
- nextRunAt
- lastError
- consecutiveFailures
- createdAt
Error:
type: object
properties:
error:
type: object
properties:
code:
type: string
description: Machine-readable error code, such as `validation_bad_request` or `jobs_not_found`.
message:
type: string
description: Human-readable explanation.
requestId:
type: string
description: Unique id for this request. Include it when contacting support.
details:
description: Structured context for some errors, such as per-field validation issues.
upgrade:
$ref: '#/components/schemas/PlanUpgradeHint'
required:
- code
- message
- requestId
required:
- error
description: The error envelope returned by every non-2xx response.
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.
externalDocs:
description: Cavuno API reference
url: https://cavuno.com/docs/api