Purchase Documents
Purchase documents connect a ticket to your purchasing system (Coupa, SAP Ariba, an ERP…). They are read with the ticket itself, in the purchases property of Get a ticket and of the Tickets List, and written with the endpoints of this page.
Rules and errors
Purchase documents are the procurement papers attached to a ticket: a purchase request (purchase_request), a purchase order (purchase_order) and a goods receipt (goods_receipt, which attests that the intervention was carried out; it is not an invoice validation). The sequence request → order → receipt is not enforced.
- A document is always issued by your own account, the buyer. The supplier is the contractor assigned to the ticket, frozen as soon as it is known; a purchase order is then visible to that supplier.
- Only the buyer can modify or delete a document. A supplier that received a purchase order gets
403. - Nothing can be written while the ticket is a draft, canceled or declined for your account (
409). Finished and closed tickets stay writable, so a goods receipt can be added after the intervention. - At most 200 live documents per buyer and per ticket.
- Reading goes through
purchases[]of Get a ticket and Tickets List. Every addition triggers theticket.purchase_addedwebhook.
The write endpoints return the document as purchase, with id; in purchases[] of the ticket the same document is keyed by _id and carries source and s3_key in addition. Every error is returned as { "error": "...", "code": "..." }:
| Status | Code | When |
|---|---|---|
400 | purchase_invalid_ticket_id, purchase_invalid_purchase_id | Malformed id in the route |
422 | Validation error on the body: { "message": "Validation error", "errors": [{ "path", "message" }] } | |
403 | purchase_no_access | Your account is not the buyer of the document |
404 | purchase_ticket_not_found | Ticket unknown, deleted, or your account has no access to it |
404 | purchase_not_found | Document unknown, deleted, or not visible to your account |
409 | purchase_ticket_state_forbidden | Ticket draft, canceled or declined for your account |
409 | purchase_duplicate_reference | Another live document of yours has this kind and reference on the ticket (update only: create returns the existing one) |
409 | purchase_goods_receipt_already_exists | The purchase order already has a goods receipt |
413 | purchase_file_limit_file_size | File larger than 20 MB |
415 | purchase_file_type | File is not a PDF, PNG or JPEG |
422 | purchase_order_link_only_on_goods_receipt | purchase_order_id sent on a document that is not a goods receipt |
422 | purchase_order_not_found | purchase_order_id is not one of your purchase orders on this ticket |
422 | purchase_quote_not_found | quote_id is not a quote of the ticket |
422 | purchase_too_many | 200 live documents already on this ticket for your account |
502, 504 | purchase_unavailable, purchase_timeout | The purchase service did not answer. Retry later: creation is idempotent, so a retry is safe |
Create a purchase document
This endpoint creates a purchase document on a ticket, issued by your account. The body is JSON; the file is attached afterwards with Attach a file.
Route Parameters
- Name
ticket_id- Type
- ObjectID
- Description
The ticket id
Required parameters
- Name
kind- Type
- string
- Description
purchase_request,purchase_orderorgoods_receipt.
- Name
reference- Type
- string
- Description
Reference of the document (max 100 chars). Unique per kind among the live documents of your account on this ticket.
Optional parameters
- Name
source_system- Type
- string
- Description
System the document comes from, for example your procurement tool (max 50 chars).
- Name
quote_id- Type
- string
- Description
Id of a quote of the ticket the document relates to. Must exist on the ticket (
422otherwise).
- Name
purchase_order_id- Type
- string
- Description
Goods receipt only: id of the purchase order it closes. Must be one of your purchase orders on this ticket (
422otherwise), and a purchase order can have only one goods receipt (409).
- Name
date- Type
- string => ISODate
- Description
Date of the document. Defaults to now.
- Name
amount- Type
- number
- Description
Amount, from 0 to 1 000 000 000.
nullif unknown.
Behavior
- Idempotent: if your account already has a live document of the same
kindandreferenceon this ticket, it is returned unchanged with200andcreated: false. The other fields of the request are ignored; use Update a purchase document to correct it. - The supplier of the document is the contractor currently assigned to the ticket, if any.
Responses
201— created, returns{ "purchase": { ... }, "created": true }200— already existed, returns{ "purchase": { ... }, "created": false }400,404,409,422,502,504— see Purchase documents
Request
curl -X POST https://api.bob-desk.com/api/v3.0/tickets/:ticket_id/purchases \
-H "Content-Type: application/json" \
-H "Authorization: Bearer {token}" \
-d '{
"kind": "purchase_order",
"reference": "PO-2026-00042",
"source_system": "coupa",
"quote_id": "63725f259ab7e72a4fb4ae69",
"date": "2026-10-01T00:00:00.000Z",
"amount": 1000
}'
Response
{
"purchase": {
"id": "68e0a1b29ab7e72a4fb4af01",
"ticket_id": "63725f259ab7e72a4fb4ae69",
"kind": "purchase_order",
"buyer_company_id": "5d9b5b5b9ab7e72a4fb4ae69",
"supplier_company_id": "5d973899ecb060003525ddec",
"reference": "PO-2026-00042",
"source_system": "coupa",
"quote_id": "63725f259ab7e72a4fb4ae69",
"purchase_order_id": null,
"date": "2026-10-01T00:00:00.000Z",
"amount": 1000,
"document_id": null,
"from_api": true,
"created_at": "2026-10-01T08:00:00.000Z",
"created_by": "5d9b5b5b9ab7e72a4fb4ae68",
"updated_at": "2026-10-01T08:00:00.000Z",
"updated_by": "5d9b5b5b9ab7e72a4fb4ae68",
"editable": true
},
"created": true
}
Update a purchase document
This endpoint updates a purchase document issued by your account. Only the fields sent are changed; kind cannot change (a purchase request never becomes a purchase order). JSON body.
Route Parameters
- Name
ticket_id- Type
- ObjectID
- Description
The ticket id
- Name
purchase_id- Type
- ObjectID
- Description
The purchase document id
Optional parameters
- Name
reference- Type
- string
- Description
New reference (max 100 chars).
409if another live document of yours has the same kind and reference on this ticket.
- Name
source_system- Type
- string
- Description
Max 50 chars. Send
nullto clear it.
- Name
quote_id- Type
- string
- Description
Quote of the ticket, or
nullto unlink.
- Name
purchase_order_id- Type
- string
- Description
Goods receipt only. Purchase order of yours on this ticket, or
nullto unlink.
- Name
date- Type
- string => ISODate
- Description
Date of the document.
- Name
amount- Type
- number
- Description
From 0 to 1 000 000 000, or
nullto clear it.
Responses
200— returns{ "purchase": { ... } }, unchanged if nothing differed403— your account is not the buyer of the document400,404,409,422,502,504— see Purchase documents
Request
curl -X PATCH https://api.bob-desk.com/api/v3.0/tickets/:ticket_id/purchases/:purchase_id \
-H "Content-Type: application/json" \
-H "Authorization: Bearer {token}" \
-d '{
"amount": 1250,
"source_system": null
}'
Delete a purchase document
This endpoint deletes a purchase document issued by your account. The deletion is soft: the document disappears from purchases[] and its reference becomes available again; its file, if any, stays in the Documents tab of the ticket.
Route Parameters
- Name
ticket_id- Type
- ObjectID
- Description
The ticket id
- Name
purchase_id- Type
- ObjectID
- Description
The purchase document id
Responses
204— deleted403— your account is not the buyer of the document400,404,409,502,504— see Purchase documents
Request
curl -X DELETE https://api.bob-desk.com/api/v3.0/tickets/:ticket_id/purchases/:purchase_id \
-H "Authorization: Bearer {token}"
Attach a file to a purchase document
This endpoint attaches the file of a purchase document issued by your account, as multipart/form-data. The file is added to the Documents tab of the ticket, tagged with the kind of the document; the file of a purchase order is shared with the supplier. Sending a new file replaces the current one on the document, the previous file staying in the Documents tab.
Route Parameters
- Name
ticket_id- Type
- ObjectID
- Description
The ticket id
- Name
purchase_id- Type
- ObjectID
- Description
The purchase document id
Properties of the request (Required)
- Name
file- Type
- File
- Description
PDF, PNG or JPEG, max 20 MB.
Responses
200— returns{ "purchase": { ... } }withdocument_idset400— no file sent (purchase_file_required)413— file larger than 20 MB415— file is not a PDF, PNG or JPEG403,404,409,502,504— see Purchase documents
Request
curl -X POST https://api.bob-desk.com/api/v3.0/tickets/:ticket_id/purchases/:purchase_id/document \
-H "Authorization: Bearer {token}" \
-F "file=@/path/to/PO-2026-00042.pdf"
Recommended workflow for a purchasing integration
This is the workflow we recommend to raise a purchase in your purchasing system (Coupa, SAP Ariba, an ERP…) when a quote is validated in Bob! Desk, and to send the purchase documents back to the ticket.
- Find the validated quotes. Every day, call
GET /api/v3.0/tickets?status=validatedand keep the quotes withvalidated: true. Deduplicate onquotes[]._id: one quote must raise only one purchase request.validatedis a transient status (it ends as soon as the work is scheduled), so also run a weekly catch-up withGET /api/v3.0/tickets?updated_after={7 days ago, in milliseconds}and readquotes[].validated_at. Never callupdated_afteralone with a date far in the past. - Choose the requester. Raise the purchase request on behalf of the ticket's
validated_bywhen it is set, otherwise of itscreator. A contractor user does not exist in your purchasing system: the person on your side who validated the ticket carries the request. - Get the quote file. Get a ticket returns a
download_urlon each quote. From the list, passquotes[].s3_keyto Download link of a document. The signed links are served fromdocuments.bob-desk.com: allow this host in your outgoing network rules. - Match the supplier. Store your supplier number in a custom field of the contractors and read it from
fieldson Contractors. - Send the purchase documents back. Create the purchase request, then the purchase order with Create a purchase document (with
quote_id), and attach its PDF. Later, record the goods receipt withpurchase_order_id. Creation is idempotent: sending the same document again is safe. - Follow the changes. Subscribe to the
ticket.purchase_addedwebhook to be notified of each new purchase document.
