> ## Documentation Index
> Fetch the complete documentation index at: https://docs.spott.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Create a candidate to-do

> Creates a new candidate to-do. Assigns a to-do to the candidate. A to-do of type `VISIT_URL` carries the link the candidate must open and requires `url`; a to-do of type `UPLOAD_ATTACHMENT` asks the candidate for a document and requires `attachmentType`. Documents themselves are not exchanged through this API.



## OpenAPI

````yaml /api-reference/openapi.json post /candidates/{id}/todos
openapi: 3.1.0
info:
  title: Spott API Reference
  version: '0.1'
servers:
  - url: https://api.gospott.com
security: []
tags: []
paths:
  /candidates/{id}/todos:
    post:
      tags:
        - Candidate Portal
      summary: Create a candidate to-do
      description: >-
        Creates a new candidate to-do. Assigns a to-do to the candidate. A to-do
        of type `VISIT_URL` carries the link the candidate must open and
        requires `url`; a to-do of type `UPLOAD_ATTACHMENT` asks the candidate
        for a document and requires `attachmentType`. Documents themselves are
        not exchanged through this API.
      operationId: createCandidateTodo
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateCandidateTodoDto'
      responses:
        '201':
          description: The to-do was created. Returns its id.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreateRecordResponseDto'
          headers:
            RateLimit:
              $ref: '#/components/headers/RateLimit'
            RateLimit-Policy:
              $ref: '#/components/headers/RateLimit-Policy'
        '400':
          description: >-
            Bad request - the payload does not match the to-do `type`: a to-do
            of type `VISIT_URL` requires `url` and does not accept
            `attachmentType`, a to-do of type `UPLOAD_ATTACHMENT` requires
            `attachmentType` and does not accept `url`. Also returned when the
            requested custom `attachmentType` does not exist or cannot be
            requested from a candidate.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExceptionBaseDto'
          headers:
            RateLimit:
              $ref: '#/components/headers/RateLimit'
            RateLimit-Policy:
              $ref: '#/components/headers/RateLimit-Policy'
        '401':
          description: Unauthorized - invalid or missing authentication
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExceptionBaseDto'
          headers:
            RateLimit:
              $ref: '#/components/headers/RateLimit'
            RateLimit-Policy:
              $ref: '#/components/headers/RateLimit-Policy'
        '403':
          description: The feature is not enabled for your organization.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExceptionBaseDto'
          headers:
            RateLimit:
              $ref: '#/components/headers/RateLimit'
            RateLimit-Policy:
              $ref: '#/components/headers/RateLimit-Policy'
        '404':
          description: >-
            Candidate not found, or `applicationId` does not point to an
            application of this candidate
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExceptionBaseDto'
          headers:
            RateLimit:
              $ref: '#/components/headers/RateLimit'
            RateLimit-Policy:
              $ref: '#/components/headers/RateLimit-Policy'
        '409':
          description: Conflict
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GlobalConflictApiErrorDto'
          headers:
            RateLimit:
              $ref: '#/components/headers/RateLimit'
            RateLimit-Policy:
              $ref: '#/components/headers/RateLimit-Policy'
        '429':
          description: >-
            Too Many Requests. The published limit for this endpoint is 600
            requests per minute per workspace. The limit in force for your
            workspace can differ from it in either direction — a workspace on a
            free trial can be capped lower, and a higher limit can be arranged
            through support — so the headers, not this number, are
            authoritative. Read the RateLimit-Policy response header for the
            quota actually in force and RateLimit for what is left of the
            current window; on a 429, Retry-After gives the seconds to wait.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExceptionBaseDto'
          headers:
            RateLimit:
              $ref: '#/components/headers/RateLimit'
            RateLimit-Policy:
              $ref: '#/components/headers/RateLimit-Policy'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
        '500':
          description: Error response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/InternalServerErrorDto'
          headers:
            RateLimit:
              $ref: '#/components/headers/RateLimit'
            RateLimit-Policy:
              $ref: '#/components/headers/RateLimit-Policy'
      security:
        - x-api-key: []
components:
  schemas:
    CreateCandidateTodoDto:
      oneOf:
        - $ref: '#/components/schemas/CreateVisitUrlCandidateTodoDto'
        - $ref: '#/components/schemas/CreateUploadAttachmentCandidateTodoDto'
    CreateRecordResponseDto:
      type: object
      properties:
        id:
          type: string
          minLength: 1
      required:
        - id
    ExceptionBaseDto:
      type: object
      properties:
        status:
          type: integer
          minimum: -9007199254740991
          maximum: 9007199254740991
        category:
          $ref: '#/components/schemas/ApiErrorCategoryDto'
        message:
          type: string
        requestId:
          type: string
      required:
        - status
        - message
        - requestId
    GlobalConflictApiErrorDto:
      anyOf:
        - $ref: '#/components/schemas/ResourceAlreadyExistsApiErrorDto'
        - $ref: '#/components/schemas/ResourceLockTimeoutApiErrorDto'
    InternalServerErrorDto:
      type: object
      properties:
        status:
          type: number
          const: 500
        category:
          type: string
          enum:
            - INTERNAL_ERROR
        message:
          type: string
          enum:
            - Internal server error
        requestId:
          type: string
      required:
        - status
        - category
        - message
        - requestId
    CreateVisitUrlCandidateTodoDto:
      type: object
      properties:
        message:
          anyOf:
            - $ref: '#/components/schemas/CreateCandidateTodoMessageDto'
            - type: 'null'
          description: Subject and body describing the to-do.
        dueDate:
          anyOf:
            - $ref: '#/components/schemas/DateISO'
            - type: 'null'
          description: >-
            ISO 8601 timestamp by which the candidate should have completed the
            to-do.
        applicationId:
          anyOf:
            - type: string
              minLength: 1
            - type: 'null'
          description: >-
            Id of the application this to-do relates to. Must be an application
            of the same candidate.
        type:
          type: string
          enum:
            - VISIT_URL
          description: Points the candidate to a link.
        url:
          type: string
          maxLength: 2048
          format: uri
          description: Link the candidate must open.
      required:
        - message
        - dueDate
        - applicationId
        - type
        - url
      additionalProperties: false
    CreateUploadAttachmentCandidateTodoDto:
      type: object
      properties:
        message:
          anyOf:
            - $ref: '#/components/schemas/CreateCandidateTodoMessageDto'
            - type: 'null'
          description: Subject and body describing the to-do.
        dueDate:
          anyOf:
            - $ref: '#/components/schemas/DateISO'
            - type: 'null'
          description: >-
            ISO 8601 timestamp by which the candidate should have completed the
            to-do.
        applicationId:
          anyOf:
            - type: string
              minLength: 1
            - type: 'null'
          description: >-
            Id of the application this to-do relates to. Must be an application
            of the same candidate.
        type:
          type: string
          enum:
            - UPLOAD_ATTACHMENT
          description: Asks the candidate for a document.
        attachmentType:
          $ref: '#/components/schemas/CandidateTodoAttachmentTypeInputDto'
          description: >-
            Kind of document expected from the candidate, a predefined
            attachment type (`{ "variant": "system", "name": "CV" }`) or a
            custom, tenant-defined attachment type (`{ "variant": "custom",
            "id": "..." }`).
      required:
        - message
        - dueDate
        - applicationId
        - type
        - attachmentType
      additionalProperties: false
    ApiErrorCategoryDto:
      type: string
      enum:
        - MALFORMED_REQUEST
        - SCHEMA_VALIDATION_FAILED
        - DOMAIN_VALIDATION_FAILED
        - OPERATION_LIMIT_EXCEEDED
        - AUTHENTICATION_REQUIRED
        - AUTHENTICATION_FAILED
        - SECURITY_CHALLENGE_REQUIRED
        - SECURITY_CHALLENGE_FAILED
        - PERMISSION_DENIED
        - ENTITLEMENT_REQUIRED
        - PROVIDER_AUTHENTICATION_REQUIRED
        - PROVIDER_AUTHENTICATION_FAILED
        - CONFIGURATION_REQUIRED
        - RESOURCE_NOT_FOUND
        - RESOURCE_ALREADY_EXISTS
        - RESOURCE_EXPIRED
        - RESOURCE_BUSY
        - RESOURCE_STATE_CONFLICT
        - QUOTA_EXCEEDED
        - RATE_LIMITED
        - UNSUPPORTED_OPERATION
        - UPSTREAM_REJECTED
        - TIMEOUT
        - TEMPORARILY_UNAVAILABLE
        - INTERNAL_ERROR
    ResourceAlreadyExistsApiErrorDto:
      type: object
      properties:
        status:
          type: number
          const: 409
        category:
          type: string
          enum:
            - RESOURCE_ALREADY_EXISTS
        message:
          type: string
          enum:
            - Resource already exists
        requestId:
          type: string
      required:
        - status
        - category
        - message
        - requestId
    ResourceLockTimeoutApiErrorDto:
      type: object
      properties:
        status:
          type: number
          const: 409
        category:
          type: string
          enum:
            - RESOURCE_BUSY
        message:
          type: string
          enum:
            - >-
              Lock could not be acquired for this resource, please try again
              later.
        requestId:
          type: string
      required:
        - status
        - category
        - message
        - requestId
    CreateCandidateTodoMessageDto:
      type: object
      properties:
        subject:
          anyOf:
            - type: string
              minLength: 1
              maxLength: 255
            - type: 'null'
          description: Short title of the to-do, shown to the candidate.
        body:
          anyOf:
            - type: string
              minLength: 1
              maxLength: 20000
            - type: 'null'
          description: Longer explanation of what the candidate has to do.
      required:
        - subject
        - body
    DateISO:
      format: date-time
      anyOf:
        - type: string
          format: date-time
          pattern: >-
            ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d(?:\.\d+)?(?:Z))$
        - type: string
          format: date
          pattern: >-
            ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))$
    CandidateTodoAttachmentTypeInputDto:
      oneOf:
        - $ref: '#/components/schemas/CandidateTodoCustomAttachmentTypeInputDto'
        - $ref: '#/components/schemas/CandidateTodoSystemAttachmentTypeDto'
      description: >-
        The kind of document requested from the candidate, either a predefined
        attachment type (`{ variant: "system", name }`) or a custom,
        tenant-defined attachment type (`{ variant: "custom", id }`).
    CandidateTodoCustomAttachmentTypeInputDto:
      type: object
      properties:
        variant:
          type: string
          enum:
            - custom
        id:
          type: string
          minLength: 1
          description: >-
            Id of a custom, tenant-defined attachment type. It must be available
            for the CANDIDATE record type; list the available types on `GET
            /attachments/types`.
      required:
        - variant
        - id
    CandidateTodoSystemAttachmentTypeDto:
      type: object
      properties:
        variant:
          type: string
          enum:
            - system
        name:
          type: string
          enum:
            - UNASSIGNED
            - CV
            - COVER_LETTER
            - PERSONALITY_TEST
            - QUESTIONNAIRE
            - TRANSCRIPT
          description: Predefined attachment type the candidate must supply.
      required:
        - variant
        - name
  headers:
    RateLimit:
      description: >-
        Quota state for this endpoint, as an RFC 8941 structured field
        (draft-ietf-httpapi-ratelimit-headers). The quoted value names the
        policy — the operationId of the endpoint the counters belong to. `r` is
        how many requests are left in the current window, `t` how many seconds
        remain before it resets.
      schema:
        type: string
      example: '"getCandidates";r=417;t=23'
    RateLimit-Policy:
      description: >-
        The policy the RateLimit header reports against, as an RFC 8941
        structured field. `q` is the quota — the requests allowed per window —
        and `w` the window length in seconds. Limits are per endpoint, so the
        quota differs between operations. This is the quota in force for the
        calling workspace, which can be lower or higher than the default
        published on the operation; treat it as authoritative.
      schema:
        type: string
      example: '"getCandidates";q=600;w=60'
    Retry-After:
      description: >-
        Seconds to wait before retrying. Sent on 429 responses; it matches the
        `t` parameter of the RateLimit header.
      schema:
        type: integer
      example: 23
  securitySchemes:
    x-api-key:
      type: apiKey
      name: x-api-key
      in: header
      description: >-
        API key for authentication. Get your API key from Settings → API Keys in
        your Spott dashboard.

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.