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

# Create jobs

> Creates one job (the job object as the body) or several (`{"jobs": [...]}`).

A job whose externalId matches an existing job of the company is updated with the fields sent instead of creating a duplicate (upsert), and is set back to normal if it was archived. A job that was deleted in Hellotracks stays deleted: it is not changed, and the answer lists it with `status: "deleted"`. The answer lists one job per job sent, in the same order.

Jobs without a location get the location of their place, else their address is geocoded in the background, so the response can still show location 0/0. Jobs that are assigned and dated drop the worker's saved route for that day, workers get the updated jobs, and the company's job webhooks fire. Unknown fields are refused with 400.



## OpenAPI

````yaml v1/openapi.json POST /jobs
openapi: 3.1.0
info:
  title: Hellotracks REST API
  version: '1'
  description: >-
    Plain REST resources over HTTPS, for tools, automations and services. The ht
    command line is built on it.
  contact:
    email: api@hellotracks.com
servers:
  - url: https://api.hellotracks.com/v1
security:
  - ApiKey: []
tags:
  - name: Sign-up and sign-in
    description: >-
      Create a company or get an API key by an emailed code, without a key; and
      check which account a key belongs to.
  - name: Company
    description: The company the API key belongs to, with its settings and teams.
  - name: Members
    description: 'The people in the company: admins, operators and workers.'
  - name: API keys
    description: 'The company''s API keys: list, create, revoke and delete.'
  - name: Jobs
    description: 'Jobs: work at a location on a date, assigned to a member.'
  - name: Places
    description: 'Places: customer sites and other saved locations with a check-in radius.'
  - name: Zones
    description: 'Zones: the company''s geofences.'
  - name: Routing
    description: Route optimization for one member's day.
  - name: ETA pages
    description: >-
      Live ETA pages (eta.page links) that show a job's recipient when the
      worker arrives.
  - name: Maps
    description: Rendered maps as GeoJSON, and public map share links.
  - name: Timesheets
    description: Clock-in and clock-out entries of members, with totals, and editing them.
  - name: Forms
    description: >-
      Form templates, submitted forms, attachments for file and image fields,
      and forms linked to places and jobs.
  - name: Alerts
    description: >-
      Alert rules (configs) that watch members, places and zones, and the alerts
      (instances) they raised.
  - name: Reports
    description: >-
      Reports: the newer reporting API (catalog, preview, run, saved) and the
      older one (types, generate, jobs, uploads, recurring).
  - name: Webhooks
    description: URLs Hellotracks calls when jobs, check-ins, clock-ins or alerts happen.
paths:
  /jobs:
    post:
      tags:
        - Jobs
      summary: Create jobs
      description: >-
        Creates one job (the job object as the body) or several (`{"jobs":
        [...]}`).


        A job whose externalId matches an existing job of the company is updated
        with the fields sent instead of creating a duplicate (upsert), and is
        set back to normal if it was archived. A job that was deleted in
        Hellotracks stays deleted: it is not changed, and the answer lists it
        with `status: "deleted"`. The answer lists one job per job sent, in the
        same order.


        Jobs without a location get the location of their place, else their
        address is geocoded in the background, so the response can still show
        location 0/0. Jobs that are assigned and dated drop the worker's saved
        route for that day, workers get the updated jobs, and the company's job
        webhooks fire. Unknown fields are refused with 400.
      operationId: createJobs
      requestBody:
        required: true
        content:
          application/json:
            schema:
              oneOf:
                - $ref: '#/components/schemas/JobInput'
                - $ref: '#/components/schemas/JobsCreateBatch'
            example:
              jobs:
                - externalId: ORDER-1001
                  title: Install water heater
                  address: 500 Market St, San Francisco, CA 94105
                  assigneeId: a1b2c3
                  date: '2026-11-02'
                  timeWindow:
                    start: '09:00'
                    end: '12:00'
                  onSiteDurationSeconds: 3600
                  priority: 2
                  notes: Ring at the side door.
                  contact:
                    name: Sam Lee
                    phone: '+14155550102'
                    email: sam@example.com
                  customFields:
                    PO: '4711'
      responses:
        '201':
          description: Created (or updated by externalId).
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                  - error
                  - meta
                properties:
                  data:
                    $ref: '#/components/schemas/JobItems'
                  error:
                    type: 'null'
                  meta:
                    $ref: '#/components/schemas/Meta'
              example:
                data:
                  items:
                    - id: '650000000000000000000001'
                      externalId: ORDER-1001
                      title: Install water heater
                      address: 500 Market St, San Francisco, CA 94105
                      notes: Ring at the side door.
                      issueText: ''
                      createdAt: 1793520000000
                      updatedAt: 1793523600000
                      scheduledAt: 0
                      assignedAt: 1793520000000
                      acceptedAt: 0
                      completedAt: 0
                      failedAt: 0
                      progress: scheduled
                      date: '2026-11-02'
                      priority: 2
                      timeWindow:
                        start: '09:00'
                        end: '12:00'
                      onSiteDurationSeconds: 3600
                      location:
                        lat: 37.7897
                        lng: -122.4
                      assignee:
                        id: a1b2c3
                        username: alex@example.com
                        name: Alex Rivera
                        email: alex@example.com
                        phone: '+14155550101'
                      placeId: p1q2r3
                      contact:
                        name: Sam Lee
                        phone: '+14155550102'
                        email: sam@example.com
                      customFields:
                        PO: '4711'
                      linkedForms:
                        - id: f1a2b3
                          title: Install checklist
                      etaSessionId: eta_k7m2p9x4q8r3
                      etaUrl: https://eta.page/K7M2-P9X4-Q8R3
                error: null
                meta:
                  path: /v1/jobs
                  method: POST
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/ServerError'
components:
  schemas:
    JobInput:
      type: object
      additionalProperties: false
      description: >-
        Job fields for create and update. Unknown fields are refused with 400.
        On update only the fields sent change.
      properties:
        id:
          type: string
          description: Job id. Only used by PATCH /jobs (single form); ignored elsewhere.
        title:
          type: string
        externalId:
          type: string
          description: >-
            Your own id. On POST /jobs an existing job of the company with the
            same externalId is updated instead of creating a new one.
        address:
          type: string
          description: Geocoded in the background when no location is given.
        location:
          type: object
          additionalProperties: false
          properties:
            lat:
              type:
                - number
                - string
              description: Number or numeric string.
            lng:
              type:
                - number
                - string
              description: Number or numeric string.
        placeId:
          type: string
          description: >-
            Link the job to a place. A job without location takes the place's
            location, a job without title the place's name.
        assigneeId:
          type: string
          description: >-
            Member id to assign. An empty string unassigns. Do not send together
            with assigneeUsername.
        assigneeUsername:
          type: string
          description: >-
            Member username to assign. An unknown username is ignored. Do not
            send together with assigneeId.
        date:
          type:
            - string
            - 'null'
          format: date
          description: >-
            YYYY-MM-DD. An empty string removes the date; null leaves it as it
            is.
        timeWindow:
          type:
            - object
            - 'null'
          additionalProperties: false
          properties:
            start:
              type: string
              description: HH:mm (00:00 to 24:00).
            end:
              type: string
              description: HH:mm (00:00 to 24:00).
        onSiteDurationSeconds:
          type:
            - integer
            - string
            - 'null'
          minimum: 0
          description: Non-negative integer (or digits as a string).
        priority:
          type:
            - integer
            - string
            - 'null'
          minimum: 0
          maximum: 10
          description: 0 to 10 (or digits as a string).
        notes:
          type: string
        contact:
          type:
            - object
            - 'null'
          additionalProperties: false
          properties:
            name:
              type: string
            phone:
              type: string
            email:
              type: string
        customFields:
          type: object
          additionalProperties:
            type: string
          description: >-
            Replaces the job's whole set of custom fields: names left out are
            removed. Values are stored as text.
      example:
        externalId: ORDER-1001
        title: Install water heater
        address: 500 Market St, San Francisco, CA 94105
        assigneeId: a1b2c3
        date: '2026-11-02'
        timeWindow:
          start: '09:00'
          end: '12:00'
        onSiteDurationSeconds: 3600
        priority: 2
        notes: Ring at the side door.
        contact:
          name: Sam Lee
          phone: '+14155550102'
          email: sam@example.com
        customFields:
          PO: '4711'
    JobsCreateBatch:
      type: object
      additionalProperties: false
      required:
        - jobs
      properties:
        jobs:
          type: array
          items:
            $ref: '#/components/schemas/JobInput'
    JobItems:
      type: object
      required:
        - items
      properties:
        items:
          type: array
          items:
            $ref: '#/components/schemas/Job'
    Meta:
      type: object
      properties:
        path:
          type: string
          description: The request path, without /v1.
          example: /jobs
        method:
          type: string
          example: GET
    Job:
      type: object
      required:
        - id
        - progress
        - timeWindow
        - location
        - assignee
        - contact
        - customFields
        - linkedForms
      properties:
        id:
          type: string
          description: Job id (24 hex characters).
        externalId:
          type: string
          description: Your own id for the job; left out when not set.
        title:
          type: string
        address:
          type: string
        notes:
          type: string
          description: Notes from the dispatcher to the worker.
        issueText:
          type: string
          description: Text the worker entered on the job.
        createdAt:
          type: integer
          format: int64
          description: ms timestamp.
        updatedAt:
          type: integer
          format: int64
          description: >-
            ms timestamp: the latest of saved, created, assigned, accepted,
            scheduled, check-in, check-out, failed and completed.
        scheduledAt:
          type: integer
          format: int64
          description: ms timestamp, 0 when not set.
        assignedAt:
          type: integer
          format: int64
          description: ms timestamp, 0 when not set.
        acceptedAt:
          type: integer
          format: int64
          description: ms timestamp, 0 when not accepted.
        completedAt:
          type: integer
          format: int64
          description: ms timestamp of success, 0 when not completed.
        failedAt:
          type: integer
          format: int64
          description: ms timestamp of failure, 0 when not failed.
        progress:
          $ref: '#/components/schemas/JobProgress'
        status:
          type: string
          enum:
            - archived
            - deleted
          description: >-
            Only on archived or deleted jobs; normal jobs have no status. A
            deleted job only shows up in the answer to a create whose externalId
            matches it.
        date:
          type:
            - string
            - 'null'
          format: date
          description: YYYY-MM-DD, null when the job has no date.
        priority:
          type: integer
          minimum: 0
          maximum: 10
        timeWindow:
          $ref: '#/components/schemas/JobTimeWindow'
        onSiteDurationSeconds:
          type: integer
          minimum: 0
        location:
          $ref: '#/components/schemas/JobLocation'
        assignee:
          $ref: '#/components/schemas/JobAssignee'
        placeId:
          type: string
          description: The place the job belongs to; left out when none.
        contact:
          $ref: '#/components/schemas/JobContact'
        customFields:
          type: object
          additionalProperties:
            type: string
          description: Custom field names and values.
        linkedForms:
          type: array
          items:
            $ref: '#/components/schemas/JobLinkedForm'
        etaSessionId:
          type: string
          description: >-
            Id of the job's active ETA session (not revoked, not expired); left
            out when there is none.
        etaUrl:
          type: string
          format: uri
          description: Public eta.page link of that session; left out when there is none.
      example:
        id: '650000000000000000000001'
        externalId: ORDER-1001
        title: Install water heater
        address: 500 Market St, San Francisco, CA 94105
        notes: Ring at the side door.
        issueText: ''
        createdAt: 1793520000000
        updatedAt: 1793523600000
        scheduledAt: 0
        assignedAt: 1793520000000
        acceptedAt: 0
        completedAt: 0
        failedAt: 0
        progress: scheduled
        date: '2026-11-02'
        priority: 2
        timeWindow:
          start: '09:00'
          end: '12:00'
        onSiteDurationSeconds: 3600
        location:
          lat: 37.7897
          lng: -122.4
        assignee:
          id: a1b2c3
          username: alex@example.com
          name: Alex Rivera
          email: alex@example.com
          phone: '+14155550101'
        placeId: p1q2r3
        contact:
          name: Sam Lee
          phone: '+14155550102'
          email: sam@example.com
        customFields:
          PO: '4711'
        linkedForms:
          - id: f1a2b3
            title: Install checklist
        etaSessionId: eta_k7m2p9x4q8r3
        etaUrl: https://eta.page/K7M2-P9X4-Q8R3
    ErrorEnvelope:
      type: object
      properties:
        data:
          type: 'null'
        error:
          $ref: '#/components/schemas/Error'
        meta:
          $ref: '#/components/schemas/Meta'
    JobProgress:
      type: string
      enum:
        - unassigned
        - scheduled
        - accepted
        - in_progress
        - success
        - failed
      description: >-
        Derived from the job's timestamps: success when completed, failed when
        marked failed, in_progress when accepted and checked in, accepted when
        accepted, unassigned when nobody is assigned, otherwise scheduled
        (assigned, not yet accepted).
    JobTimeWindow:
      type: object
      description: >-
        Time window on the job's date, local time of the company, as HH:mm
        strings (24:00 allowed as the end).
      properties:
        start:
          type: string
          pattern: ^\d{2}:\d{2}$
          description: HH:mm. 00:00 when not set.
          example: '09:00'
        end:
          type: string
          pattern: ^\d{2}:\d{2}$
          description: HH:mm. 24:00 when not set.
          example: '12:00'
    JobLocation:
      type: object
      description: >-
        Destination coordinates. 0/0 when the job has no location yet (for
        example while its address is still being geocoded).
      properties:
        lat:
          type: number
          format: double
          example: 37.7897
        lng:
          type: number
          format: double
          example: -122.4
    JobAssignee:
      type: object
      description: The assigned member. Empty object when the job is unassigned.
      properties:
        id:
          type: string
          description: Member id.
        username:
          type: string
        name:
          type: string
        email:
          type: string
        phone:
          type: string
    JobContact:
      type: object
      description: On-site contact. Fields that are not set are left out.
      properties:
        name:
          type: string
        phone:
          type: string
        email:
          type: string
    JobLinkedForm:
      type: object
      properties:
        id:
          type: string
          description: Form template id.
        title:
          type: string
    Error:
      type: object
      required:
        - code
        - message
      properties:
        code:
          type: string
          description: Stable error code for programs.
          enum:
            - invalid_request
            - invalid_format
            - invalid_data
            - invalid_api_key
            - unknown_user
            - authentication_failed
            - forbidden
            - not_found
            - already_exists
            - throttled
            - not_ready
            - clock_invalid
            - force_logout
            - internal_error
            - unknown_error
        message:
          type: string
          description: Why it failed, for people.
  responses:
    BadRequest:
      description: The request is invalid.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
          example:
            data: null
            error:
              code: invalid_request
              message: date must be YYYY-MM-DD
            meta:
              path: /jobs
              method: GET
    Unauthorized:
      description: The API key is missing or invalid.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
          example:
            data: null
            error:
              code: invalid_api_key
              message: Invalid Hellotracks API key.
            meta:
              path: /jobs
              method: GET
    Forbidden:
      description: The key may not do this.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
          example:
            data: null
            error:
              code: forbidden
              message: No permission.
            meta:
              path: /jobs
              method: GET
    TooManyRequests:
      description: Too many requests; slow down.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
          example:
            data: null
            error:
              code: throttled
              message: Too many requests.
            meta:
              path: /jobs
              method: GET
    ServerError:
      description: Hellotracks failed; retry later.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
          example:
            data: null
            error:
              code: internal_error
              message: Hellotracks API is temporarily unavailable.
            meta:
              path: /jobs
              method: GET
  securitySchemes:
    ApiKey:
      type: apiKey
      in: header
      name: API-Key
      description: >-
        Your API key, from More → API & Integrations in the dashboard, or from
        ht signup / ht auth login.

````

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