Service Promos API

18 PracBill API endpoints for service promos. Base URL https://billing.pracbill.com.au/api.

List the promo assignment rows on a engineering service, newest `date_start` first. Soft-deleted rows and rows whose promo is deleted or inactive are excluded. The service must exist and be non-deleted, and its customer must belong to the token's department (engineering has no department column of its own), otherwise every method fails with `service not found`.

GET /{api_key}/engineering/{enid}/service_promos

Responses

  • 200 — The promo assignment rows with their promo

Create a promo assignment row on a engineering service. `service_promo_id` and `date_start` are required; the promo must be non-deleted, active and in the token's department. `date_end`, when given, may not precede `date_start`. When `date_end` is omitted (or sent as null) it is derived as `date_start + duration_months - 1 day` from the promo, exactly like the staff UI; a promo without a duration leaves the assignment open-ended. A PUT that clears `date_end` re-derives it the same way. The whole body is validated before anything is written; an unknown key (including `deptid`, `enid`, `id`) rejects the request. The service must exist and be non-deleted, and its customer must belong to the token's department (engineering has no department column of its own), otherwise every method fails with `service not found`.

POST /{api_key}/engineering/{enid}/service_promos

Request body

FieldTypeDescription
service_promo_idintegerA non-deleted, active promo in the token's department
date_startstringYYYY-MM-DD
date_endstringYYYY-MM-DD, not before date_start; null / "" clears it (assignments then re-derive it from the promo duration)
activebooleanBoolean-like (true/false, 1/0, "yes"/"no"); defaults to true on create

Responses

  • 200 — The created promo assignment row

Soft-delete a promo assignment row (sets `deleted = 1`). Only the relationship row is touched — the promo and the engineering service are left as they were. Allowed even when the row's promo has since been deleted or deactivated, so orphaned rows stay removable. The service must exist and be non-deleted, and its customer must belong to the token's department (engineering has no department column of its own), otherwise every method fails with `service not found`.

DELETE /{api_key}/engineering/{enid}/service_promos/{id}

Responses

  • 200 — Deletion result

Fetch a single promo assignment row. Fails with `service promo relationship not found` when the row does not exist, is deleted, belongs to another department, sits under a different engineering service than the one in the URL, or its promo is deleted / inactive. The service must exist and be non-deleted, and its customer must belong to the token's department (engineering has no department column of its own), otherwise every method fails with `service not found`.

GET /{api_key}/engineering/{enid}/service_promos/{id}

Responses

  • 200 — The promo assignment row

Identical to the PUT form of this endpoint, for clients that cannot send PUT.

POST /{api_key}/engineering/{enid}/service_promos/{id}

Request body

FieldTypeDescription
service_promo_idintegerA non-deleted, active promo in the token's department
date_startstringYYYY-MM-DD
date_endstringYYYY-MM-DD, not before date_start; null / "" clears it (assignments then re-derive it from the promo duration)
activebooleanBoolean-like (true/false, 1/0, "yes"/"no"); defaults to true on create

Responses

  • 200 — The updated promo assignment row

Partially update a promo assignment row: only the fields present in the body change. The same validation as create applies (minus the required-field rule); `date_end` / `date_start` are checked against the stored values for anything not sent, and a new `service_promo_id` must be a live, active promo in the token's department. When `date_end` is omitted (or sent as null) it is derived as `date_start + duration_months - 1 day` from the promo, exactly like the staff UI; a promo without a duration leaves the assignment open-ended. A PUT that clears `date_end` re-derives it the same way. The service must exist and be non-deleted, and its customer must belong to the token's department (engineering has no department column of its own), otherwise every method fails with `service not found`.

PUT /{api_key}/engineering/{enid}/service_promos/{id}

Request body

FieldTypeDescription
service_promo_idintegerA non-deleted, active promo in the token's department
date_startstringYYYY-MM-DD
date_endstringYYYY-MM-DD, not before date_start; null / "" clears it (assignments then re-derive it from the promo duration)
activebooleanBoolean-like (true/false, 1/0, "yes"/"no"); defaults to true on create

Responses

  • 200 — The updated promo assignment row

List service promos. Only non-deleted promos belonging to the API token's department are returned (inactive promos are included). Results are ordered by promo name, then id.

GET /{api_key}/service_promos

Responses

  • 200 — A page of service promos

Create a service promo. `name`, `discount_type` and `discount_value` are required. The body is validated as a whole before anything is written: any key outside the writable set (including `deptid`, `deleted` and `id`) or any invalid value rejects the request with `success: false`. `deptid` is taken from the API token. `signup_code` is trimmed and uppercased exactly as the UI does.

POST /{api_key}/service_promos

Request body

FieldTypeDescription
namestringPromo name. Required on create; trimmed; cannot be blank.
descriptionstring
signup_codestringOptional customer-entered code. Trimmed and uppercased on save, identically to the UI; send an empty string or null to clear it.
discount_typestring (percent | fixed)Required on create.
discount_valuenumberRequired on create. Non-negative; stored to two decimal places.
duration_monthsintegerPositive integer, or null / empty string for an ongoing promo.
stackableobjectBoolean-like: true/false, 1/0, "1"/"0", "true"/"false", "yes"/"no". Defaults to false on create.
activeobjectBoolean-like (see `stackable`). Defaults to true on create.

Responses

  • 200 — The created service promo

Soft-delete a service promo (sets `deleted = 1`). The row is never physically removed and the promo disappears from the list and get endpoints. Service-type availability, assignment and inclusion rows referencing the promo are left untouched.

DELETE /{api_key}/service_promos/{id}

Responses

  • 200 — Deletion result

Fetch a single service promo. Fails with `service promo not found` when the promo does not exist, is deleted, or belongs to another department — the three cases are indistinguishable.

GET /{api_key}/service_promos/{id}

Responses

  • 200 — The service promo

Identical to the PUT form of this endpoint, for clients that cannot send PUT.

POST /{api_key}/service_promos/{id}

Request body

FieldTypeDescription
namestringPromo name. Required on create; trimmed; cannot be blank.
descriptionstring
signup_codestringOptional customer-entered code. Trimmed and uppercased on save, identically to the UI; send an empty string or null to clear it.
discount_typestring (percent | fixed)Required on create.
discount_valuenumberRequired on create. Non-negative; stored to two decimal places.
duration_monthsintegerPositive integer, or null / empty string for an ongoing promo.
stackableobjectBoolean-like: true/false, 1/0, "1"/"0", "true"/"false", "yes"/"no". Defaults to false on create.
activeobjectBoolean-like (see `stackable`). Defaults to true on create.

Responses

  • 200 — The updated service promo

Partially update a service promo: only the fields present in the body change. The same validation as create applies (minus the required-field rule) and the whole request is rejected, with nothing written, if any field is invalid or unsupported.

PUT /{api_key}/service_promos/{id}

Request body

FieldTypeDescription
namestringPromo name. Required on create; trimmed; cannot be blank.
descriptionstring
signup_codestringOptional customer-entered code. Trimmed and uppercased on save, identically to the UI; send an empty string or null to clear it.
discount_typestring (percent | fixed)Required on create.
discount_valuenumberRequired on create. Non-negative; stored to two decimal places.
duration_monthsintegerPositive integer, or null / empty string for an ongoing promo.
stackableobjectBoolean-like: true/false, 1/0, "1"/"0", "true"/"false", "yes"/"no". Defaults to false on create.
activeobjectBoolean-like (see `stackable`). Defaults to true on create.

Responses

  • 200 — The updated service promo

List the promo availability rows on a service type, newest `date_start` first. Soft-deleted rows and rows whose promo is deleted or inactive are excluded. The service type must exist, be non-deleted and belong to the token's department, otherwise every method fails with `service type not found`.

GET /{api_key}/service_types/{esid}/service_promos

Responses

  • 200 — The promo availability rows with their promo

Create a promo availability row on a service type. `service_promo_id` and `date_start` are required; the promo must be non-deleted, active and in the token's department. `date_end`, when given, may not precede `date_start`. Omitting `date_end` (or sending null) leaves the availability open-ended; it is never derived from the promo duration. The whole body is validated before anything is written; an unknown key (including `deptid`, `esid`, `id`) rejects the request. The service type must exist, be non-deleted and belong to the token's department, otherwise every method fails with `service type not found`.

POST /{api_key}/service_types/{esid}/service_promos

Request body

FieldTypeDescription
service_promo_idintegerA non-deleted, active promo in the token's department
date_startstringYYYY-MM-DD
date_endstringYYYY-MM-DD, not before date_start; null / "" clears it (assignments then re-derive it from the promo duration)
activebooleanBoolean-like (true/false, 1/0, "yes"/"no"); defaults to true on create

Responses

  • 200 — The created promo availability row

Soft-delete a promo availability row (sets `deleted = 1`). Only the relationship row is touched — the promo and the service type are left as they were. Allowed even when the row's promo has since been deleted or deactivated, so orphaned rows stay removable. The service type must exist, be non-deleted and belong to the token's department, otherwise every method fails with `service type not found`.

DELETE /{api_key}/service_types/{esid}/service_promos/{id}

Responses

  • 200 — Deletion result

Fetch a single promo availability row. Fails with `service promo relationship not found` when the row does not exist, is deleted, belongs to another department, sits under a different service type than the one in the URL, or its promo is deleted / inactive. The service type must exist, be non-deleted and belong to the token's department, otherwise every method fails with `service type not found`.

GET /{api_key}/service_types/{esid}/service_promos/{id}

Responses

  • 200 — The promo availability row

Identical to the PUT form of this endpoint, for clients that cannot send PUT.

POST /{api_key}/service_types/{esid}/service_promos/{id}

Request body

FieldTypeDescription
service_promo_idintegerA non-deleted, active promo in the token's department
date_startstringYYYY-MM-DD
date_endstringYYYY-MM-DD, not before date_start; null / "" clears it (assignments then re-derive it from the promo duration)
activebooleanBoolean-like (true/false, 1/0, "yes"/"no"); defaults to true on create

Responses

  • 200 — The updated promo availability row

Partially update a promo availability row: only the fields present in the body change. The same validation as create applies (minus the required-field rule); `date_end` / `date_start` are checked against the stored values for anything not sent, and a new `service_promo_id` must be a live, active promo in the token's department. Omitting `date_end` (or sending null) leaves the availability open-ended; it is never derived from the promo duration. The service type must exist, be non-deleted and belong to the token's department, otherwise every method fails with `service type not found`.

PUT /{api_key}/service_types/{esid}/service_promos/{id}

Request body

FieldTypeDescription
service_promo_idintegerA non-deleted, active promo in the token's department
date_startstringYYYY-MM-DD
date_endstringYYYY-MM-DD, not before date_start; null / "" clears it (assignments then re-derive it from the promo duration)
activebooleanBoolean-like (true/false, 1/0, "yes"/"no"); defaults to true on create

Responses

  • 200 — The updated promo availability row

← All API groups