Cavuno Categories API

Create, list, update, and delete job categories, and manage their aliases.

Operations 12

GET /taxonomies/categories List categories #
POST /taxonomies/categories Create a category #
GET /taxonomies/categories/{id} Retrieve a category #
PATCH /taxonomies/categories/{id} Update a category #
DELETE /taxonomies/categories/{id} Delete a category #
GET /taxonomies/categories/tree Retrieve the category tree #
POST /taxonomies/categories/{id}/aliases Add category aliases #
GET /taxonomies/categories/{id}/aliases/{alias} Retrieve a category alias #
DELETE /taxonomies/categories/{id}/aliases/{alias} Remove a category alias #
GET /taxonomies/categories/{id}/merge-preview Preview a category merge #
POST /taxonomies/categories/{id}/merge Merge a category #
POST /taxonomies/categories/merges/{mergeId}/undo Undo a category merge #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/cavuno-com:cavuno-com-categories-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

cavuno-com-categories-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: Cavuno v1 REST Categories 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: Categories
  description: Create, list, update, and delete job categories, and manage their aliases.
paths:
  /taxonomies/categories:
    get:
      summary: List categories
      description: Returns a paginated list of your categories. Nesting is expressed by `parentId`; for the assembled hierarchy use `GET /v1/taxonomies/categories/tree`.
      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
          minLength: 1
          maxLength: 200
          description: Full-text search over the taxonomy name (matches name tokens, not slug).
        required: false
        description: Full-text search over the taxonomy name (matches name tokens, not slug).
        name: search
        in: query
      - schema:
          type: string
          minLength: 1
          description: 'Filter to the direct children of a parent category id. The literal "null" is a deprecated alias for `topLevel=true`. Combined with `search`, a page may be short or empty while hasMore is true: keep paging until hasMore is false.'
        required: false
        description: 'Filter to the direct children of a parent category id. The literal "null" is a deprecated alias for `topLevel=true`. Combined with `search`, a page may be short or empty while hasMore is true: keep paging until hasMore is false.'
        name: parentId
        in: query
      - schema:
          type: string
          enum:
          - 'true'
          - 'false'
          description: When `true`, return only top-level categories (those with no parent). Cannot be combined with a `parentId` category id.
        required: false
        description: When `true`, return only top-level categories (those with no parent). Cannot be combined with a `parentId` category id.
        name: topLevel
        in: query
      responses:
        '200':
          description: Successful response.
          content:
            application/json:
              schema:
                type: object
                properties:
                  object:
                    type: string
                    enum:
                    - list
                  url:
                    type: string
                    example: /v1/taxonomies/categories
                  hasMore:
                    type: boolean
                  nextCursor:
                    type:
                    - string
                    - 'null'
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Category'
                required:
                - object
                - url
                - hasMore
                - nextCursor
                - data
        default:
          description: An error. Every non-2xx response uses the same envelope; see the Errors section of the introduction.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-audience: operator
      tags:
      - Categories
      operationId: listTaxonomiesCategories
    post:
      summary: Create a category
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateCategoryBody'
      responses:
        '201':
          description: Created.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Category'
        '409':
          description: Slug taken.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-audience: operator
      tags:
      - Categories
      operationId: createTaxonomiesCategory
  /taxonomies/categories/{id}:
    get:
      summary: Retrieve a category
      parameters:
      - schema:
          type: string
          minLength: 1
          description: The category's opaque object id (not the slug). Use list/create responses to obtain it.
        required: true
        description: The category's opaque object id (not the slug). Use list/create responses to obtain it.
        name: id
        in: path
      responses:
        '200':
          description: Successful response.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Category'
        '404':
          description: Not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-audience: operator
      tags:
      - Categories
      operationId: getTaxonomiesCategory
    patch:
      summary: Update a category
      description: Partial update. parentId cycles rejected with taxonomy_parent_cycle. Slug change dual-writes aliases and cascades job denorm.
      parameters:
      - schema:
          type: string
          minLength: 1
          description: The category's opaque object id (not the slug). Use list/create responses to obtain it.
        required: true
        description: The category's opaque object id (not the slug). Use list/create responses to obtain it.
        name: id
        in: path
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateCategoryBody'
      responses:
        '200':
          description: Successful response.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Category'
        '400':
          description: Parent cycle.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: Slug taken.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-audience: operator
      tags:
      - Categories
      operationId: updateTaxonomiesCategory
    delete:
      summary: Delete a category
      description: Deletes the category and its direct children only (1-level cascade). Grandchildren are not removed.
      parameters:
      - schema:
          type: string
          minLength: 1
          description: The category's opaque object id (not the slug). Use list/create responses to obtain it.
        required: true
        description: The category's opaque object id (not the slug). Use list/create responses to obtain it.
        name: id
        in: path
      responses:
        '204':
          description: Deleted.
        '404':
          description: Not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-audience: operator
      tags:
      - Categories
      operationId: deleteTaxonomiesCategory
  /taxonomies/categories/tree:
    get:
      summary: Retrieve the category tree
      description: 'Returns top-level categories with their descendants nested under `children`. The same data as `GET /v1/taxonomies/categories`, assembled into a hierarchy for menus and pickers. Not paginated: capped at 1000 nodes, above which it returns 400. Page the flat list for larger taxonomies.'
      responses:
        '200':
          description: Successful response.
          content:
            application/json:
              schema:
                type: object
                properties:
                  object:
                    type: string
                    enum:
                    - list
                  url:
                    type: string
                    example: /v1/taxonomies/categories/tree
                  hasMore:
                    type: boolean
                  nextCursor:
                    type:
                    - string
                    - 'null'
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Category'
                required:
                - object
                - url
                - hasMore
                - nextCursor
                - data
        '400':
          description: Tree too large.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-audience: operator
      tags:
      - Categories
      operationId: getTaxonomiesCategoryTree
  /taxonomies/categories/{id}/aliases:
    post:
      summary: Add category aliases
      parameters:
      - schema:
          type: string
          minLength: 1
          description: The category's opaque object id (not the slug). Use list/create responses to obtain it.
        required: true
        description: The category's opaque object id (not the slug). Use list/create responses to obtain it.
        name: id
        in: path
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AddCategoryAliasesBody'
      responses:
        '200':
          description: Successful response.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Category'
        '409':
          description: Alias taken.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-audience: operator
      tags:
      - Categories
      operationId: createTaxonomiesCategoryAlias
  /taxonomies/categories/{id}/aliases/{alias}:
    get:
      summary: Retrieve a category alias
      description: Checks that `alias` is an alias of the category and returns the category, in the same shape as the add aliases response. The alias is normalized to a slug before matching, as it is when you remove one.
      parameters:
      - schema:
          type: string
          minLength: 1
        required: true
        name: id
        in: path
      - schema:
          type: string
          minLength: 1
        required: true
        name: alias
        in: path
      responses:
        '200':
          description: Successful response.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Category'
        '404':
          description: Category or alias not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-audience: operator
      tags:
      - Categories
      operationId: getTaxonomiesCategoryAlias
    delete:
      summary: Remove a category alias
      parameters:
      - schema:
          type: string
          minLength: 1
        required: true
        name: id
        in: path
      - schema:
          type: string
          minLength: 1
        required: true
        name: alias
        in: path
      responses:
        '204':
          description: Removed.
        '404':
          description: Not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-audience: operator
      tags:
      - Categories
      operationId: deleteTaxonomiesCategoryAlias
  /taxonomies/categories/{id}/merge-preview:
    get:
      summary: Preview a category merge
      description: Counts what merging this category into `intoId` would change. Writes nothing.
      parameters:
      - schema:
          type: string
          minLength: 1
          description: The category's opaque object id (not the slug). Use list/create responses to obtain it.
        required: true
        description: The category's opaque object id (not the slug). Use list/create responses to obtain it.
        name: id
        in: path
      - schema:
          type: string
          minLength: 1
          description: The id of the term that would survive the merge.
        required: true
        description: The id of the term that would survive the merge.
        name: intoId
        in: query
      responses:
        '200':
          description: Successful response.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TaxonomyMergePreview'
        '400':
          description: The two terms cannot be merged.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-audience: operator
      tags:
      - Categories
      operationId: previewTaxonomiesCategoryMerge
  /taxonomies/categories/{id}/merge:
    post:
      summary: Merge a category
      description: 'Merges this category into `intoId`: its jobs, aliases, translations and subcategories move to the surviving category, and this category is deleted. Jobs are retagged in the background. Undo is available for 30 days once `undo.status` is `ready`. A category cannot be merged into itself or into one of its subcategories.'
      parameters:
      - schema:
          type: string
          minLength: 1
          description: The category's opaque object id (not the slug). Use list/create responses to obtain it.
        required: true
        description: The category's opaque object id (not the slug). Use list/create responses to obtain it.
        name: id
        in: path
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/MergeTaxonomyBody'
      responses:
        '200':
          description: Successful response.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TaxonomyMerge'
        '400':
          description: The two terms cannot be merged.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: The merge conflicted with another change.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-audience: operator
      tags:
      - Categories
      operationId: mergeTaxonomiesCategory
  /taxonomies/categories/merges/{mergeId}/undo:
    post:
      summary: Undo a category merge
      description: Recreates the merged category with its slug, name, aliases and translations and subcategories, and retags the jobs the merge retagged. Available for 30 days, unless the surviving category has since been renamed, deleted or merged with another.
      parameters:
      - schema:
          type: string
          minLength: 1
          description: The merge id returned by the merge endpoint.
        required: true
        description: The merge id returned by the merge endpoint.
        name: mergeId
        in: path
      responses:
        '200':
          description: Successful response.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TaxonomyMerge'
        '404':
          description: Not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: The merge can no longer be undone.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-audience: operator
      tags:
      - Categories
      operationId: undoTaxonomiesCategoryMerge
components:
  schemas:
    Category:
      type: object
      properties:
        id:
          type: string
        object:
          type: string
          enum:
          - category
        name:
          type: string
        slug:
          type: string
        aliasSlugs:
          type: array
          items:
            type: string
        sourceLocale:
          type:
          - string
          - 'null'
        parentId:
          type:
          - string
          - 'null'
        createdAt:
          type: string
        children:
          type: array
          items: {}
      required:
      - id
      - object
      - name
      - slug
      - aliasSlugs
      - sourceLocale
      - parentId
      - createdAt
    TaxonomyMerge:
      type: object
      properties:
        id:
          type: string
        object:
          type: string
          enum:
          - taxonomy_merge
        kind:
          type: string
          enum:
          - category
          - skill
        from:
          type: object
          properties:
            id:
              type: string
            name:
              type: string
            slug:
              type: string
          required:
          - id
          - name
          - slug
          description: The term merged away. Its id no longer exists after the merge.
        into:
          type: object
          properties:
            id:
              type: string
            name:
              type: string
            slug:
              type: string
          required:
          - id
          - name
          - slug
          description: The term that survived.
        undo:
          type: object
          properties:
            status:
              type: string
              enum:
              - applying
              - ready
              - over_cap
              - undoing
              - undone
              - expired
              description: '`applying` while jobs are being retagged; `ready` once undo is available; `over_cap` when the merge retagged too many jobs to undo; `undoing` / `undone` after an undo; `expired` after 30 days.'
            expiresAt:
              type: string
              description: When undo stops being available.
            restoredId:
              type:
              - string
              - 'null'
              description: After an undo, the id of the recreated term.
          required:
          - status
          - expiresAt
          - restoredId
        createdAt:
          type: string
      required:
      - id
      - object
      - kind
      - from
      - into
      - undo
      - createdAt
    AddCategoryAliasesBody:
      type: object
      properties:
        aliases:
          type: array
          items:
            type: string
            minLength: 1
          minItems: 1
          maxItems: 20
      required:
      - aliases
      additionalProperties: false
    MergeTaxonomyBody:
      type: object
      properties:
        intoId:
          type: string
          minLength: 1
          description: The id of the term that survives the merge.
      required:
      - intoId
      additionalProperties: false
    UpdateCategoryBody:
      type: object
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 200
        slug:
          type: string
          minLength: 1
          maxLength: 120
          pattern: ^[a-z0-9-]+$
        parentId:
          anyOf:
          - type: string
            minLength: 1
          - {}
        sourceLocale:
          type: string
          minLength: 2
          maxLength: 16
      additionalProperties: false
    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.'
    TaxonomyMergePreview:
      type: object
      properties:
        object:
          type: string
          enum:
          - taxonomy_merge_preview
        kind:
          type: string
          enum:
          - category
          - skill
        from:
          type: object
          properties:
            id:
              type: string
            name:
              type: string
            slug:
              type: string
            jobCount:
              type: integer
              description: Live jobs tagged with this term.
          required:
          - id
          - name
          - slug
          - jobCount
        into:
          type: object
          properties:
            id:
              type: string
            name:
              type: string
            slug:
              type: string
            jobCount:
              type: integer
              description: Live jobs tagged with this term.
          required:
          - id
          - name
          - slug
          - jobCount
        childCount:
          type: integer
          description: Subcategories that move to the surviving category.
        undoAvailable:
          type: boolean
          description: 'Whether the merge will be undoable: `from` has no more live jobs than `undoJobCap`.'
        undoJobCap:
          type: integer
        undoWindowDays:
          type: integer
      required:
      - object
      - kind
      - from
      - into
      - childCount
      - undoAvailable
      - undoJobCap
      - undoWindowDays
    CreateCategoryBody:
      type: object
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 200
        slug:
          type: string
          minLength: 1
          maxLength: 120
          pattern: ^[a-z0-9-]+$
        parentId:
          type: string
          minLength: 1
        aliasSlugs:
          type: array
          items:
            type: string
            minLength: 1
          maxItems: 20
        sourceLocale:
          type: string
          minLength: 2
          maxLength: 16
      required:
      - name
      additionalProperties: false
    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