---
openapi: 3.1.0
info:
  title: boekt.online API
  version: '1'
  description: |
    Read and change the data of one organisation in boekt.online.

    Every request needs an API key in the Authorization header. A key only sees its own organisation. Lists are paginated and may be filtered by their last update time.
servers:
- url: "/"
security:
- bearerAuth: []
paths:
  "/api/v1/customers":
    get:
      summary: List clients
      responses:
        '200':
          description: A page of records
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/CustomerList"
        '401':
          "$ref": "#/components/responses/Unauthorized"
        '429':
          "$ref": "#/components/responses/RateLimitExceeded"
        '500':
          "$ref": "#/components/responses/InternalError"
        '400':
          "$ref": "#/components/responses/InvalidParameter"
      parameters:
      - "$ref": "#/components/parameters/page"
      - "$ref": "#/components/parameters/per_page"
      - "$ref": "#/components/parameters/updated_since"
    post:
      summary: Create a client
      responses:
        '201':
          description: The new record
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/CustomerResponse"
        '401':
          "$ref": "#/components/responses/Unauthorized"
        '429':
          "$ref": "#/components/responses/RateLimitExceeded"
        '500':
          "$ref": "#/components/responses/InternalError"
        '403':
          "$ref": "#/components/responses/Forbidden"
        '415':
          "$ref": "#/components/responses/UnsupportedMediaType"
        '422':
          "$ref": "#/components/responses/UnprocessableRequest"
      parameters:
      - "$ref": "#/components/parameters/idempotency_key"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                customer:
                  "$ref": "#/components/schemas/CustomerInput"
  "/api/v1/customers/{id}":
    get:
      summary: Show a client
      responses:
        '200':
          description: The requested record
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/CustomerResponse"
        '401':
          "$ref": "#/components/responses/Unauthorized"
        '429':
          "$ref": "#/components/responses/RateLimitExceeded"
        '500':
          "$ref": "#/components/responses/InternalError"
        '404':
          "$ref": "#/components/responses/NotFound"
      parameters:
      - "$ref": "#/components/parameters/id"
    patch:
      summary: Change a client
      responses:
        '200':
          description: The changed record
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/CustomerResponse"
        '401':
          "$ref": "#/components/responses/Unauthorized"
        '429':
          "$ref": "#/components/responses/RateLimitExceeded"
        '500':
          "$ref": "#/components/responses/InternalError"
        '404':
          "$ref": "#/components/responses/NotFound"
        '403':
          "$ref": "#/components/responses/Forbidden"
        '415':
          "$ref": "#/components/responses/UnsupportedMediaType"
        '422':
          "$ref": "#/components/responses/UnprocessableRequest"
      parameters:
      - "$ref": "#/components/parameters/id"
      - "$ref": "#/components/parameters/idempotency_key"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                customer:
                  "$ref": "#/components/schemas/CustomerInput"
    put:
      summary: Change a client
      responses:
        '200':
          description: The changed record
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/CustomerResponse"
        '401':
          "$ref": "#/components/responses/Unauthorized"
        '429':
          "$ref": "#/components/responses/RateLimitExceeded"
        '500':
          "$ref": "#/components/responses/InternalError"
        '404':
          "$ref": "#/components/responses/NotFound"
        '403':
          "$ref": "#/components/responses/Forbidden"
        '415':
          "$ref": "#/components/responses/UnsupportedMediaType"
        '422':
          "$ref": "#/components/responses/UnprocessableRequest"
      parameters:
      - "$ref": "#/components/parameters/id"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                customer:
                  "$ref": "#/components/schemas/CustomerInput"
  "/api/v1/projects":
    get:
      summary: List projects
      responses:
        '200':
          description: A page of records
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ProjectList"
        '401':
          "$ref": "#/components/responses/Unauthorized"
        '429':
          "$ref": "#/components/responses/RateLimitExceeded"
        '500':
          "$ref": "#/components/responses/InternalError"
        '400':
          "$ref": "#/components/responses/InvalidParameter"
      parameters:
      - "$ref": "#/components/parameters/page"
      - "$ref": "#/components/parameters/per_page"
      - "$ref": "#/components/parameters/updated_since"
    post:
      summary: Create a project for a client of this organisation
      responses:
        '201':
          description: The new record
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ProjectResponse"
        '401':
          "$ref": "#/components/responses/Unauthorized"
        '429':
          "$ref": "#/components/responses/RateLimitExceeded"
        '500':
          "$ref": "#/components/responses/InternalError"
        '404':
          "$ref": "#/components/responses/NotFound"
        '403':
          "$ref": "#/components/responses/Forbidden"
        '415':
          "$ref": "#/components/responses/UnsupportedMediaType"
        '422':
          "$ref": "#/components/responses/UnprocessableRequest"
      parameters:
      - "$ref": "#/components/parameters/idempotency_key"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                project:
                  "$ref": "#/components/schemas/ProjectInput"
  "/api/v1/projects/{id}":
    get:
      summary: Show a project
      responses:
        '200':
          description: The requested record
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ProjectResponse"
        '401':
          "$ref": "#/components/responses/Unauthorized"
        '429':
          "$ref": "#/components/responses/RateLimitExceeded"
        '500':
          "$ref": "#/components/responses/InternalError"
        '404':
          "$ref": "#/components/responses/NotFound"
      parameters:
      - "$ref": "#/components/parameters/id"
    patch:
      summary: Change a project
      responses:
        '200':
          description: The changed record
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ProjectResponse"
        '401':
          "$ref": "#/components/responses/Unauthorized"
        '429':
          "$ref": "#/components/responses/RateLimitExceeded"
        '500':
          "$ref": "#/components/responses/InternalError"
        '404':
          "$ref": "#/components/responses/NotFound"
        '403':
          "$ref": "#/components/responses/Forbidden"
        '415':
          "$ref": "#/components/responses/UnsupportedMediaType"
        '422':
          "$ref": "#/components/responses/UnprocessableRequest"
      parameters:
      - "$ref": "#/components/parameters/id"
      - "$ref": "#/components/parameters/idempotency_key"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                project:
                  "$ref": "#/components/schemas/ProjectInput"
    put:
      summary: Change a project
      responses:
        '200':
          description: The changed record
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ProjectResponse"
        '401':
          "$ref": "#/components/responses/Unauthorized"
        '429':
          "$ref": "#/components/responses/RateLimitExceeded"
        '500':
          "$ref": "#/components/responses/InternalError"
        '404':
          "$ref": "#/components/responses/NotFound"
        '403':
          "$ref": "#/components/responses/Forbidden"
        '415':
          "$ref": "#/components/responses/UnsupportedMediaType"
        '422':
          "$ref": "#/components/responses/UnprocessableRequest"
      parameters:
      - "$ref": "#/components/parameters/id"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                project:
                  "$ref": "#/components/schemas/ProjectInput"
  "/api/v1/time_entries":
    get:
      summary: List time entries
      responses:
        '200':
          description: A page of records
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/TimeEntryList"
        '401':
          "$ref": "#/components/responses/Unauthorized"
        '429':
          "$ref": "#/components/responses/RateLimitExceeded"
        '500':
          "$ref": "#/components/responses/InternalError"
        '400':
          "$ref": "#/components/responses/InvalidParameter"
      parameters:
      - "$ref": "#/components/parameters/page"
      - "$ref": "#/components/parameters/per_page"
      - "$ref": "#/components/parameters/updated_since"
  "/api/v1/time_entries/{id}":
    get:
      summary: Show a time entry
      responses:
        '200':
          description: The requested record
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/TimeEntryResponse"
        '401':
          "$ref": "#/components/responses/Unauthorized"
        '429':
          "$ref": "#/components/responses/RateLimitExceeded"
        '500':
          "$ref": "#/components/responses/InternalError"
        '404':
          "$ref": "#/components/responses/NotFound"
      parameters:
      - "$ref": "#/components/parameters/id"
  "/api/v1/invoices/{id}/booked":
    patch:
      summary: Record the invoice number another package gave this invoice
      responses:
        '200':
          description: The changed invoice
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/InvoiceResponse"
        '401':
          "$ref": "#/components/responses/Unauthorized"
        '429':
          "$ref": "#/components/responses/RateLimitExceeded"
        '500':
          "$ref": "#/components/responses/InternalError"
        '404':
          "$ref": "#/components/responses/NotFound"
        '403':
          "$ref": "#/components/responses/Forbidden"
        '415':
          "$ref": "#/components/responses/UnsupportedMediaType"
        '422':
          "$ref": "#/components/responses/UnprocessableRequest"
      parameters:
      - "$ref": "#/components/parameters/id"
      - "$ref": "#/components/parameters/idempotency_key"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - invoice
              properties:
                invoice:
                  type: object
                  required:
                  - external_invoice_id
                  properties:
                    external_invoice_id:
                      type: string
  "/api/v1/invoices":
    get:
      summary: List invoices issued by this organisation
      responses:
        '200':
          description: A page of records
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/InvoiceList"
        '401':
          "$ref": "#/components/responses/Unauthorized"
        '429':
          "$ref": "#/components/responses/RateLimitExceeded"
        '500':
          "$ref": "#/components/responses/InternalError"
        '400':
          "$ref": "#/components/responses/InvalidParameter"
      parameters:
      - "$ref": "#/components/parameters/page"
      - "$ref": "#/components/parameters/per_page"
      - "$ref": "#/components/parameters/updated_since"
  "/api/v1/invoices/{id}":
    get:
      summary: Show an invoice with its lines
      responses:
        '200':
          description: The requested record
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/InvoiceResponse"
        '401':
          "$ref": "#/components/responses/Unauthorized"
        '429':
          "$ref": "#/components/responses/RateLimitExceeded"
        '500':
          "$ref": "#/components/responses/InternalError"
        '404':
          "$ref": "#/components/responses/NotFound"
      parameters:
      - "$ref": "#/components/parameters/id"
  "/api/v1/services":
    get:
      summary: List services
      responses:
        '200':
          description: A page of records
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ServiceList"
        '401':
          "$ref": "#/components/responses/Unauthorized"
        '429':
          "$ref": "#/components/responses/RateLimitExceeded"
        '500':
          "$ref": "#/components/responses/InternalError"
        '400':
          "$ref": "#/components/responses/InvalidParameter"
      parameters:
      - "$ref": "#/components/parameters/page"
      - "$ref": "#/components/parameters/per_page"
      - "$ref": "#/components/parameters/updated_since"
    post:
      summary: Create a service
      responses:
        '201':
          description: The new record
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ServiceResponse"
        '401':
          "$ref": "#/components/responses/Unauthorized"
        '429':
          "$ref": "#/components/responses/RateLimitExceeded"
        '500':
          "$ref": "#/components/responses/InternalError"
        '403':
          "$ref": "#/components/responses/Forbidden"
        '415':
          "$ref": "#/components/responses/UnsupportedMediaType"
        '422':
          "$ref": "#/components/responses/UnprocessableRequest"
      parameters:
      - "$ref": "#/components/parameters/idempotency_key"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                service:
                  "$ref": "#/components/schemas/ServiceInput"
  "/api/v1/services/{id}":
    get:
      summary: Show a service
      responses:
        '200':
          description: The requested record
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ServiceResponse"
        '401':
          "$ref": "#/components/responses/Unauthorized"
        '429':
          "$ref": "#/components/responses/RateLimitExceeded"
        '500':
          "$ref": "#/components/responses/InternalError"
        '404':
          "$ref": "#/components/responses/NotFound"
      parameters:
      - "$ref": "#/components/parameters/id"
    patch:
      summary: Change a service
      responses:
        '200':
          description: The changed record
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ServiceResponse"
        '401':
          "$ref": "#/components/responses/Unauthorized"
        '429':
          "$ref": "#/components/responses/RateLimitExceeded"
        '500':
          "$ref": "#/components/responses/InternalError"
        '404':
          "$ref": "#/components/responses/NotFound"
        '403':
          "$ref": "#/components/responses/Forbidden"
        '415':
          "$ref": "#/components/responses/UnsupportedMediaType"
        '422':
          "$ref": "#/components/responses/UnprocessableRequest"
      parameters:
      - "$ref": "#/components/parameters/id"
      - "$ref": "#/components/parameters/idempotency_key"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                service:
                  "$ref": "#/components/schemas/ServiceInput"
    put:
      summary: Change a service
      responses:
        '200':
          description: The changed record
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ServiceResponse"
        '401':
          "$ref": "#/components/responses/Unauthorized"
        '429':
          "$ref": "#/components/responses/RateLimitExceeded"
        '500':
          "$ref": "#/components/responses/InternalError"
        '404':
          "$ref": "#/components/responses/NotFound"
        '403':
          "$ref": "#/components/responses/Forbidden"
        '415':
          "$ref": "#/components/responses/UnsupportedMediaType"
        '422':
          "$ref": "#/components/responses/UnprocessableRequest"
      parameters:
      - "$ref": "#/components/parameters/id"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                service:
                  "$ref": "#/components/schemas/ServiceInput"
  "/api/v1/materials":
    get:
      summary: List materials
      responses:
        '200':
          description: A page of records
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/MaterialList"
        '401':
          "$ref": "#/components/responses/Unauthorized"
        '429':
          "$ref": "#/components/responses/RateLimitExceeded"
        '500':
          "$ref": "#/components/responses/InternalError"
        '400':
          "$ref": "#/components/responses/InvalidParameter"
      parameters:
      - "$ref": "#/components/parameters/page"
      - "$ref": "#/components/parameters/per_page"
      - "$ref": "#/components/parameters/updated_since"
    post:
      summary: Create a material
      responses:
        '201':
          description: The new record
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/MaterialResponse"
        '401':
          "$ref": "#/components/responses/Unauthorized"
        '429':
          "$ref": "#/components/responses/RateLimitExceeded"
        '500':
          "$ref": "#/components/responses/InternalError"
        '403':
          "$ref": "#/components/responses/Forbidden"
        '415':
          "$ref": "#/components/responses/UnsupportedMediaType"
        '422':
          "$ref": "#/components/responses/UnprocessableRequest"
      parameters:
      - "$ref": "#/components/parameters/idempotency_key"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                material:
                  "$ref": "#/components/schemas/MaterialInput"
  "/api/v1/materials/{id}":
    get:
      summary: Show a material
      responses:
        '200':
          description: The requested record
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/MaterialResponse"
        '401':
          "$ref": "#/components/responses/Unauthorized"
        '429':
          "$ref": "#/components/responses/RateLimitExceeded"
        '500':
          "$ref": "#/components/responses/InternalError"
        '404':
          "$ref": "#/components/responses/NotFound"
      parameters:
      - "$ref": "#/components/parameters/id"
    patch:
      summary: Change a material
      responses:
        '200':
          description: The changed record
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/MaterialResponse"
        '401':
          "$ref": "#/components/responses/Unauthorized"
        '429':
          "$ref": "#/components/responses/RateLimitExceeded"
        '500':
          "$ref": "#/components/responses/InternalError"
        '404':
          "$ref": "#/components/responses/NotFound"
        '403':
          "$ref": "#/components/responses/Forbidden"
        '415':
          "$ref": "#/components/responses/UnsupportedMediaType"
        '422':
          "$ref": "#/components/responses/UnprocessableRequest"
      parameters:
      - "$ref": "#/components/parameters/id"
      - "$ref": "#/components/parameters/idempotency_key"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                material:
                  "$ref": "#/components/schemas/MaterialInput"
    put:
      summary: Change a material
      responses:
        '200':
          description: The changed record
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/MaterialResponse"
        '401':
          "$ref": "#/components/responses/Unauthorized"
        '429':
          "$ref": "#/components/responses/RateLimitExceeded"
        '500':
          "$ref": "#/components/responses/InternalError"
        '404':
          "$ref": "#/components/responses/NotFound"
        '403':
          "$ref": "#/components/responses/Forbidden"
        '415':
          "$ref": "#/components/responses/UnsupportedMediaType"
        '422':
          "$ref": "#/components/responses/UnprocessableRequest"
      parameters:
      - "$ref": "#/components/parameters/id"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                material:
                  "$ref": "#/components/schemas/MaterialInput"
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: An API key of the form gt_...
  parameters:
    id:
      name: id
      in: path
      required: true
      schema:
        type: integer
    page:
      name: page
      in: query
      schema:
        type: integer
        minimum: 1
        default: 1
    per_page:
      name: per_page
      in: query
      schema:
        type: integer
        minimum: 1
        maximum: 100
        default: 25
    updated_since:
      name: updated_since
      in: query
      description: Only records changed at or after this moment
      schema:
        type: string
        format: date-time
    idempotency_key:
      name: Idempotency-Key
      in: header
      required: false
      description: Optional unique key. Repeating the same request within 24 hours
        returns the stored response.
      schema:
        type: string
  responses:
    InvalidParameter:
      description: A parameter or the JSON request is invalid.
      content:
        application/problem+json:
          schema:
            "$ref": "#/components/schemas/Problem"
    Unauthorized:
      description: A valid API key is required for this request.
      content:
        application/problem+json:
          schema:
            "$ref": "#/components/schemas/Problem"
    Forbidden:
      description: This API key does not have write access.
      content:
        application/problem+json:
          schema:
            "$ref": "#/components/schemas/Problem"
    NotFound:
      description: The requested record does not exist in this organisation.
      content:
        application/problem+json:
          schema:
            "$ref": "#/components/schemas/Problem"
    RateLimitExceeded:
      description: Too many API requests were made in a short period.
      content:
        application/problem+json:
          schema:
            "$ref": "#/components/schemas/Problem"
    UnsupportedMediaType:
      description: Write requests with a body must use application/json.
      content:
        application/problem+json:
          schema:
            "$ref": "#/components/schemas/Problem"
    UnprocessableRequest:
      description: The data does not meet the conditions required to save it. This
        idempotency key already belongs to a different request body.
      content:
        application/problem+json:
          schema:
            "$ref": "#/components/schemas/Problem"
    InternalError:
      description: An unexpected error prevented the request from being processed.
      content:
        application/problem+json:
          schema:
            "$ref": "#/components/schemas/Problem"
  schemas:
    Problem:
      type: object
      required:
      - type
      - title
      - status
      - detail
      - code
      - instance
      properties:
        type:
          type: string
          format: uri-reference
        title:
          type: string
        status:
          type: integer
        detail:
          type: string
        code:
          type: string
          enum:
          - unauthorized
          - forbidden
          - not_found
          - invalid_parameter
          - validation_failed
          - rate_limit_exceeded
          - idempotency_key_reused
          - unsupported_media_type
          - internal_error
        instance:
          type: string
          format: uri-reference
        errors:
          type: object
          additionalProperties:
            type: array
            items:
              type: string
    Meta:
      type: object
      properties:
        page:
          type: integer
        per_page:
          type: integer
        total:
          type: integer
    CustomFields:
      type: object
      description: The organisation's active extra fields, keyed by name.
      additionalProperties: true
    InvoiceLine:
      type: object
      properties:
        type:
          type: string
        description:
          type: string
        quantity:
          type: number
        unit:
          type: string
        unit_price:
          type: number
        line_amount:
          type: number
    Customer:
      type: object
      properties:
        id:
          type: integer
        name: &1
          type: string
        email: &2
          type:
          - string
          - 'null'
        phone: &3
          type:
          - string
          - 'null'
        address: &4
          type:
          - string
          - 'null'
        external_id: &5
          type:
          - string
          - 'null'
        custom_fields:
          "$ref": "#/components/schemas/CustomFields"
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    CustomerResponse:
      type: object
      properties:
        data:
          "$ref": "#/components/schemas/Customer"
    CustomerList:
      type: object
      properties:
        data:
          type: array
          items:
            "$ref": "#/components/schemas/Customer"
        meta:
          "$ref": "#/components/schemas/Meta"
    CustomerInput:
      type: object
      properties:
        name: *1
        email: *2
        phone: *3
        address: *4
        external_id: *5
        custom_fields:
          "$ref": "#/components/schemas/CustomFields"
    Project:
      type: object
      properties:
        id:
          type: integer
        customer_id: &6
          type: integer
        name: &7
          type: string
        description: &8
          type:
          - string
          - 'null'
        active: &9
          type: boolean
        service: &10
          type:
          - string
          - 'null'
        budget_hours: &11
          type:
          - number
          - 'null'
        budget_other_costs: &12
          type:
          - number
          - 'null'
        fixed_price: &13
          type: boolean
        fixed_price_amount: &14
          type:
          - number
          - 'null'
        fixed_price_includes_materials: &15
          type: boolean
        custom_fields:
          "$ref": "#/components/schemas/CustomFields"
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    ProjectResponse:
      type: object
      properties:
        data:
          "$ref": "#/components/schemas/Project"
    ProjectList:
      type: object
      properties:
        data:
          type: array
          items:
            "$ref": "#/components/schemas/Project"
        meta:
          "$ref": "#/components/schemas/Meta"
    ProjectInput:
      type: object
      properties:
        customer_id: *6
        name: *7
        description: *8
        active: *9
        service: *10
        budget_hours: *11
        budget_other_costs: *12
        fixed_price: *13
        fixed_price_amount: *14
        fixed_price_includes_materials: *15
        custom_fields:
          "$ref": "#/components/schemas/CustomFields"
    TimeEntry:
      type: object
      properties:
        id:
          type: integer
        customer_id:
          type: integer
        project_id:
          type:
          - integer
          - 'null'
        date:
          type: string
          format: date
        hours:
          type:
          - number
          - 'null'
        break_minutes:
          type:
          - integer
          - 'null'
        comments:
          type:
          - string
          - 'null'
        service:
          type:
          - string
          - 'null'
        status:
          type: string
          enum:
          - draft
          - invoiced
        invoice_id:
          type:
          - integer
          - 'null'
        net_amount:
          type:
          - number
          - 'null'
        total_amount:
          type:
          - number
          - 'null'
        custom_fields:
          "$ref": "#/components/schemas/CustomFields"
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    TimeEntryResponse:
      type: object
      properties:
        data:
          "$ref": "#/components/schemas/TimeEntry"
    TimeEntryList:
      type: object
      properties:
        data:
          type: array
          items:
            "$ref": "#/components/schemas/TimeEntry"
        meta:
          "$ref": "#/components/schemas/Meta"
    Invoice:
      type: object
      properties:
        id:
          type: integer
        target_type:
          type: string
          enum:
          - customer
          - freelancer
        customer_id:
          type:
          - integer
          - 'null'
        vat_rate:
          type:
          - number
          - 'null'
        net_amount:
          type:
          - number
          - 'null'
        total_amount:
          type:
          - number
          - 'null'
        status:
          type: string
          enum:
          - local
          - queued
          - sending
          - sent
          - failed
          - withdrawn
        external_invoice_id:
          type:
          - string
          - 'null'
        lines:
          type: array
          items:
            "$ref": "#/components/schemas/InvoiceLine"
        custom_fields:
          "$ref": "#/components/schemas/CustomFields"
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    InvoiceResponse:
      type: object
      properties:
        data:
          "$ref": "#/components/schemas/Invoice"
    InvoiceList:
      type: object
      properties:
        data:
          type: array
          items:
            "$ref": "#/components/schemas/Invoice"
        meta:
          "$ref": "#/components/schemas/Meta"
    Service:
      type: object
      properties:
        id:
          type: integer
        name: &16
          type: string
        description: &17
          type:
          - string
          - 'null'
        unit: &18
          type:
          - string
          - 'null'
        price_excl_vat: &19
          type:
          - number
          - 'null'
        vat_rate: &20
          type:
          - number
          - 'null'
        active: &21
          type: boolean
        custom_fields:
          "$ref": "#/components/schemas/CustomFields"
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    ServiceResponse:
      type: object
      properties:
        data:
          "$ref": "#/components/schemas/Service"
    ServiceList:
      type: object
      properties:
        data:
          type: array
          items:
            "$ref": "#/components/schemas/Service"
        meta:
          "$ref": "#/components/schemas/Meta"
    ServiceInput:
      type: object
      properties:
        name: *16
        description: *17
        unit: *18
        price_excl_vat: *19
        vat_rate: *20
        active: *21
        custom_fields:
          "$ref": "#/components/schemas/CustomFields"
    Material:
      type: object
      properties:
        id:
          type: integer
        name: &22
          type: string
        unit: &23
          type:
          - string
          - 'null'
        price_excl_vat: &24
          type:
          - number
          - 'null'
        vat_rate: &25
          type:
          - number
          - 'null'
        active: &26
          type: boolean
        custom_fields:
          "$ref": "#/components/schemas/CustomFields"
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    MaterialResponse:
      type: object
      properties:
        data:
          "$ref": "#/components/schemas/Material"
    MaterialList:
      type: object
      properties:
        data:
          type: array
          items:
            "$ref": "#/components/schemas/Material"
        meta:
          "$ref": "#/components/schemas/Meta"
    MaterialInput:
      type: object
      properties:
        name: *22
        unit: *23
        price_excl_vat: *24
        vat_rate: *25
        active: *26
        custom_fields:
          "$ref": "#/components/schemas/CustomFields"
