Skip to content

Creating a request

POST {BASE_URL}/requests

A request is an enquiry — a lead that somebody in the office triages into work. It is the endpoint to use when the customer might not exist yet, which is exactly the case a website form or a marketing campaign produces.

What happens to it afterwards is the life of a request: it is read, and turned into a job, a quote, an appointment or nothing at all.

Like POST /jobs, this endpoint only creates. There is no way to read, update, cancel or delete a request through the API.

Mode A — the customer exists. Send all three IDs together:

{
"external_id": "CRM-REQUEST-10001",
"description": "Customer needs a plumbing inspection",
"source": "CRM",
"customer": {
"id": 501,
"site": { "id": 701 },
"contact": { "id": 901 }
},
"job_types": [
{ "job_type_id": 12 }
]
}

Mode B — the customer is new. Send no IDs at all, and give the details instead:

{
"external_id": "CRM-REQUEST-10002",
"description": "New customer enquiry",
"source": "CRM",
"customer": {
"name": "New Customer Ltd",
"site": {
"name": "Primary Site",
"address": "10 Main Street",
"city": "Mumbai",
"country_id": 101,
"state_id": 22,
"zip_code": "400001",
"latitude": 19.0760,
"longitude": 72.8777
},
"contact": {
"name": "Anita Sharma",
"email": "anita@example.com",
"mobile_number": "+919876543210",
"alternate_number": "+912212345678"
}
},
"job_types": []
}

Partial IDs are rejected. Either all three IDs or none of them — there is no half-way where you know the customer but not the site. If you have a customer ID and no site, look the sites up first; see finding a customer.

FieldRequiredNotes
external_idNoMust be unique if sent; a repeat returns 409.
descriptionNoWhat the enquiry is about.
sourceNoFree text naming where it came from — CRM, website, phone.
job_types[]NoMay be empty. Each entry, if present, carries a job_type_id.

source is worth filling in even though nothing forces you to. It is what lets the office tell an enquiry from your booking form apart from one somebody typed in, months after everybody has forgotten which integration was live.

FieldRequiredNotes
customer.nameYes
customer.site.nameNoDefaults to Self.
customer.site.addressYes
customer.site.cityNo
customer.site.country_idYesA positive numeric ID from GET /countries.
customer.site.state_idYesA positive numeric ID from GET /states.
customer.site.zip_codeNo
customer.site.latitude / longitudeNoDecimal degrees.
customer.contact.nameNoDefaults to Self.
customer.contact.emailConditionalOne of email or mobile_number is required.
customer.contact.mobile_numberConditionalOne of email or mobile_number is required.
customer.contact.alternate_numberNo

The two conditional rows are the ones to design around: a lead with no way to reach the person is not a lead, so the API will not take one. If your form makes both optional, make one of them required before it gets this far.

Coordinates are worth sending when you have them. An address that a map cannot resolve is what sends a fieldworker to the wrong end of a street; latitude and longitude settle it — see why a fieldworker is turning up at the wrong place.

{
"success": true,
"data": {
"id": 2564,
"external_id": "CRM-REQUEST-10001",
"label": "Req-1507",
"status": "new",
"source": "CRM",
"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": [],
"created_at": "2026-09-08T13:22:00Z"
},
"request_id": "req_f431b71597baf346",
"timestamp": "2026-09-08T13:22:00Z"
}

The same envelope as everywhere else, with the request’s own ID and label, and — in Mode B — the customer, site and contact that were created for it. Keep all three IDs: they are what a later POST /jobs for the same customer will need.