Hourtick Invoices API

Invoices through the workspace's own Stripe account (Stripe Connect). An admin connects Stripe in Settings; Stripe Invoicing issues, emails and collects each invoice; Hourtick takes no fee (Stripe's own fees apply). Lines come from billable time not invoiced yet, and sent time is locked as invoiced. Agents send on their own only inside an invoice mandate; otherwise the invoice waits for an admin's approval. Writes take an `idempotencyKey` (body) or `Idempotency-Key` header.

Business capability
Accounts Receivable Management BC-200.30

Operations 14

GET /api/v1/payments The workspace's Stripe connection: connected, ready to send invoices, and… #
PATCH /api/v1/payments VAT rate (basis points) and label, and default days until due (admins) #
DELETE /api/v1/payments Stop sending invoices through the account (admins). #
POST /api/v1/payments/connect Start or resume Stripe-hosted onboarding and get its one-time URL (admins… #
POST /api/v1/invoices/preview What an invoice would contain: lines, VAT, total, and anything that stops… #
GET /api/v1/invoices Invoices, newest first (admins; agents see their clients') #
POST /api/v1/invoices Create an invoice from a client's billable time not invoiced yet and send it… #
GET /api/v1/invoices/{id} One invoice with its lines, payment page and timesheet link #
POST /api/v1/invoices/{id}/approve Approve an agent's invoice waiting outside its mandate, and send it (admins) #
POST /api/v1/invoices/{id}/retry Send an invoice again that didn't reach Stripe (admins). #
POST /api/v1/invoices/{id}/void Void an invoice (or discard one not sent); its time becomes billable again… #
GET /api/v1/invoice-mandates What agents may invoice without approval (admins; an agent sees its own) #
POST /api/v1/invoice-mandates Let an agent send invoices on its own: clients (empty: any it works on), most… #
DELETE /api/v1/invoice-mandates/{id} Stop an invoice mandate; the agent's invoices wait for approval again (admins) #

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/hourtick:hourtick-invoices-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

hourtick-invoices-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: Hourtick Invoices API
  version: 1.0.0
  description: 'The Hourtick REST API. Authenticate with a personal API token: create one in **Settings → API tokens**, then send `Authorization: Bearer ht_…`.


    All timer changes go through **POST /api/v1/commands**, the same endpoint the web and Mac apps use. Commands are idempotent per `commandId` (use a new UUID per action, reuse it to retry safely).


    Every response carries a `Server-Time` header (epoch ms). Errors use RFC 9457 problem details.'
servers:
- url: https://hourtick.com
security:
- bearer: []
tags:
- name: Invoices
  description: Invoices through the workspace's own Stripe account (Stripe Connect). An admin connects Stripe in Settings; Stripe Invoicing issues, emails and collects each invoice; Hourtick takes no fee (Stripe's own fees apply). Lines come from billable time not invoiced yet, and sent time is locked as invoiced. Agents send on their own only inside an invoice mandate; otherwise the invoice waits for an admin's approval. Writes take an `idempotencyKey` (body) or `Idempotency-Key` header.
paths:
  /api/v1/payments:
    get:
      tags:
      - Invoices
      summary: 'The workspace''s Stripe connection: connected, ready to send invoices, and…'
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
      operationId: getApiV1Payments
      x-operation-id-source: derived
    patch:
      tags:
      - Invoices
      summary: VAT rate (basis points) and label, and default days until due (admins)
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $schema: https://json-schema.org/draft/2020-12/schema
              type: object
              properties:
                vatRateBps:
                  anyOf:
                  - type: integer
                    minimum: 0
                    maximum: 10000
                  - type: 'null'
                vatLabel:
                  type: string
                  minLength: 1
                  maxLength: 30
                defaultDueDays:
                  type: integer
                  minimum: 0
                  maximum: 365
              additionalProperties: false
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
        '409':
          description: Stripe isn't connected
      operationId: patchApiV1Payments
      x-operation-id-source: derived
    delete:
      tags:
      - Invoices
      summary: Stop sending invoices through the account (admins).
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
      operationId: deleteApiV1Payments
      x-operation-id-source: derived
  /api/v1/payments/connect:
    post:
      tags:
      - Invoices
      summary: Start or resume Stripe-hosted onboarding and get its one-time URL (admins…
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $schema: https://json-schema.org/draft/2020-12/schema
              type: object
              properties:
                country:
                  type: string
                  minLength: 2
                  maxLength: 2
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  url:
                    type: string
        '422':
          description: Country missing
      operationId: postApiV1PaymentsConnect
      x-operation-id-source: derived
  /api/v1/invoices/preview:
    post:
      tags:
      - Invoices
      summary: 'What an invoice would contain: lines, VAT, total, and anything that stops…'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $schema: https://json-schema.org/draft/2020-12/schema
              type: object
              properties:
                clientId:
                  type: string
                  format: uuid
                  pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                projectIds:
                  maxItems: 200
                  type: array
                  items:
                    type: string
                    format: uuid
                    pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                from:
                  type: string
                to:
                  type: string
                groupBy:
                  description: 'How lines are grouped: project_task (default), person, day or entry'
                  type: string
                  enum:
                  - project_task
                  - person
                  - day
                  - entry
                dueDays:
                  type: integer
                  minimum: 0
                  maximum: 365
                memo:
                  type: string
                  maxLength: 500
                idempotencyKey:
                  type: string
                  minLength: 8
                  maxLength: 100
              required:
              - clientId
              additionalProperties: false
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                description: '{ preview }'
      operationId: postApiV1InvoicesPreview
      x-operation-id-source: derived
  /api/v1/invoices:
    get:
      tags:
      - Invoices
      summary: Invoices, newest first (admins; agents see their clients')
      parameters:
      - name: clientId
        in: query
        schema:
          type: string
          format: uuid
      - name: status
        in: query
        schema:
          type: string
          enum:
          - pending_approval
          - draft
          - open
          - paid
          - void
          - uncollectible
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                description: '{ invoices }'
      operationId: getApiV1Invoices
      x-operation-id-source: derived
    post:
      tags:
      - Invoices
      summary: Create an invoice from a client's billable time not invoiced yet and send it…
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $schema: https://json-schema.org/draft/2020-12/schema
              type: object
              properties:
                clientId:
                  type: string
                  format: uuid
                  pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                projectIds:
                  maxItems: 200
                  type: array
                  items:
                    type: string
                    format: uuid
                    pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                from:
                  type: string
                to:
                  type: string
                groupBy:
                  description: 'How lines are grouped: project_task (default), person, day or entry'
                  type: string
                  enum:
                  - project_task
                  - person
                  - day
                  - entry
                dueDays:
                  type: integer
                  minimum: 0
                  maximum: 365
                memo:
                  type: string
                  maxLength: 500
                idempotencyKey:
                  type: string
                  minLength: 8
                  maxLength: 100
              required:
              - clientId
              additionalProperties: false
      responses:
        '201':
          description: OK
          content:
            application/json:
              schema:
                type: object
                description: '{ invoice }'
        '409':
          description: Stripe not connected, or the time changed or is already on another invoice
        '422':
          description: 'Not ready: e.g. no billing email or nothing to bill'
        '502':
          description: Stripe refused it; the invoice stays as not sent and can be retried
      operationId: postApiV1Invoices
      x-operation-id-source: derived
  /api/v1/invoices/{id}:
    get:
      tags:
      - Invoices
      summary: One invoice with its lines, payment page and timesheet link
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                description: '{ invoice }'
      operationId: getApiV1InvoicesById
      x-operation-id-source: derived
  /api/v1/invoices/{id}/approve:
    post:
      tags:
      - Invoices
      summary: Approve an agent's invoice waiting outside its mandate, and send it (admins)
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
      operationId: postApiV1InvoicesByIdApprove
      x-operation-id-source: derived
  /api/v1/invoices/{id}/retry:
    post:
      tags:
      - Invoices
      summary: Send an invoice again that didn't reach Stripe (admins).
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
      operationId: postApiV1InvoicesByIdRetry
      x-operation-id-source: derived
  /api/v1/invoices/{id}/void:
    post:
      tags:
      - Invoices
      summary: Void an invoice (or discard one not sent); its time becomes billable again…
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
        '409':
          description: Already paid
      operationId: postApiV1InvoicesByIdVoid
      x-operation-id-source: derived
  /api/v1/invoice-mandates:
    get:
      tags:
      - Invoices
      summary: What agents may invoice without approval (admins; an agent sees its own)
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                description: '{ mandates }'
      operationId: getApiV1InvoiceMandates
      x-operation-id-source: derived
    post:
      tags:
      - Invoices
      summary: 'Let an agent send invoices on its own: clients (empty: any it works on), most…'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $schema: https://json-schema.org/draft/2020-12/schema
              type: object
              properties:
                agentId:
                  type: string
                  format: uuid
                  pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                clientIds:
                  default: []
                  maxItems: 500
                  type: array
                  items:
                    type: string
                    format: uuid
                    pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                maxTotalMinor:
                  type: integer
                  minimum: 1
                  maximum: 100000000000
                expiresAt:
                  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: 'null'
              required:
              - agentId
              - maxTotalMinor
              additionalProperties: false
      responses:
        '201':
          description: OK
          content:
            application/json:
              schema:
                type: object
      operationId: postApiV1InvoiceMandates
      x-operation-id-source: derived
  /api/v1/invoice-mandates/{id}:
    delete:
      tags:
      - Invoices
      summary: Stop an invoice mandate; the agent's invoices wait for approval again (admins)
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
      operationId: deleteApiV1InvoiceMandatesById
      x-operation-id-source: derived
components:
  securitySchemes:
    bearer:
      type: http
      scheme: bearer
      bearerFormat: ht_… personal API token