Skip to content

Creating a job

POST {BASE_URL}/jobs

A job is created for a customer who already exists, at one of their sites, against one of their contacts. Find those three first — see finding a customer — then send their IDs.

If the customer is new, this is the wrong endpoint: create a request instead, which can carry the customer’s details and let the office turn it into work.

FieldRequiredNotes
external_idNoYour system’s own reference. Must be unique if sent; a repeat returns 409.
job_codeNoShown as the job’s label in EyeOnTask.
descriptionNoWhat the work is.
instructionNoHow to go about it — instructions for the fieldworker.
priority_idNoA numeric ID from GET /priorities. Falls back to the company’s default.
customer.idYesFrom GET /customers.
customer.site.idYesFrom GET /customers/{id}/sites.
customer.contact.idYesFrom GET /customers/{id}/contacts.
customer.name, site.name, contact.nameNoInformational only — the IDs are authoritative. A name that disagrees with its ID changes nothing.
job_types[]YesA non-empty array; each entry carries a job_type_id.
schedule.start_atNoISO-8601 with an offset or Z. Required if end_at is sent.
schedule.end_atNoOmit it and the job gets a one-hour duration.
schedule.display_textNoFree text shown beside the schedule — it appears on the scheduler strip.

Send external_id. It is optional to the API and essential to you: it is what stops a retry, a replayed webhook in your own system or a re-run batch creating the same job twice, and it is how you find the EyeOnTask job again from your side later.

Every job created through the API gets the default status Not Dispatched, and the caller cannot set or change it.

That is the right default rather than a limitation: the job is on the board for the office to look at, but nothing has gone to anybody’s phone. Somebody decides who is going and dispatches it.

From there the job’s life is inside EyeOnTask, and the API will not tell you about it — there is no endpoint to read a job or its status back.

{
"external_id": "CRM-JOB-10001",
"job_code": "JOB-10001",
"description": "Annual AC maintenance",
"instruction": "Call before arrival",
"priority_id": 2,
"customer": {
"id": 501,
"name": "Acme Services",
"site": {
"id": 701,
"name": "Main Office"
},
"contact": {
"id": 901,
"name": "Ravi Kumar"
}
},
"job_types": [
{ "job_type_id": 12 }
],
"schedule": {
"start_at": "2026-09-10T10:00:00+05:30",
"end_at": "2026-09-10T11:00:00+05:30",
"display_text": "Thursday morning"
}
}

Rules worth restating, because they are where the 422s come from:

  • A datetime must carry a timezone offset or Z.
  • end_at without start_at is rejected.
  • start_at on its own is fine — the job becomes an hour long.
{
"success": true,
"data": {
"id": 88001,
"external_id": "CRM-JOB-10001",
"label": "JOB-10001",
"status": "Not Dispatched",
"priority": { "id": 2, "name": "medium" },
"customer": { "id": 501, "name": "Acme Services" },
"site": { "id": 701, "name": "Main Office" },
"contact": {
"id": 901,
"name": "Ravi Kumar",
"email": "customer@example.com",
"mobile_number": "+919876543210"
},
"job_types": [
{ "id": 12, "name": "Air Conditioning" }
],
"schedule": {
"start": "2026-09-10T04:30:00Z",
"end": "2026-09-10T05:30:00Z",
"display_text": "Thursday morning"
},
"created_at": "2026-09-07T12:00:00Z"
},
"request_id": "req_abc123",
"timestamp": "2026-09-07T12:00:00Z"
}

The schedule comes back in UTC — the +05:30 you sent is the same moment as the Z you get.

Persist id, external_id and request_id. The first is how EyeOnTask knows the job, the second is how you do, and the third is what support needs if the two ever disagree.

Send the create with an Idempotency-Key so a timeout can be retried without risking a second job.