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 the ticket.purchase_added webhook.

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": "..." }:

StatusCodeWhen
400purchase_invalid_ticket_id, purchase_invalid_purchase_idMalformed id in the route
422Validation error on the body: { "message": "Validation error", "errors": [{ "path", "message" }] }
403purchase_no_accessYour account is not the buyer of the document
404purchase_ticket_not_foundTicket unknown, deleted, or your account has no access to it
404purchase_not_foundDocument unknown, deleted, or not visible to your account
409purchase_ticket_state_forbiddenTicket draft, canceled or declined for your account
409purchase_duplicate_referenceAnother live document of yours has this kind and reference on the ticket (update only: create returns the existing one)
409purchase_goods_receipt_already_existsThe purchase order already has a goods receipt
413purchase_file_limit_file_sizeFile larger than 20 MB
415purchase_file_typeFile is not a PDF, PNG or JPEG
422purchase_order_link_only_on_goods_receiptpurchase_order_id sent on a document that is not a goods receipt
422purchase_order_not_foundpurchase_order_id is not one of your purchase orders on this ticket
422purchase_quote_not_foundquote_id is not a quote of the ticket
422purchase_too_many200 live documents already on this ticket for your account
502, 504purchase_unavailable, purchase_timeoutThe purchase service did not answer. Retry later: creation is idempotent, so a retry is safe

POST/api/v3.0/tickets/:ticket_id/purchases

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_order or goods_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 (422 otherwise).

  • 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 (422 otherwise), 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. null if unknown.

Behavior

  • Idempotent: if your account already has a live document of the same kind and reference on this ticket, it is returned unchanged with 200 and created: 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

POST
/api/v3.0/tickets/:ticket_id/purchases
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
}

PATCH/api/v3.0/tickets/:ticket_id/purchases/:purchase_id

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). 409 if another live document of yours has the same kind and reference on this ticket.

  • Name
    source_system
    Type
    string
    Description

    Max 50 chars. Send null to clear it.

  • Name
    quote_id
    Type
    string
    Description

    Quote of the ticket, or null to unlink.

  • Name
    purchase_order_id
    Type
    string
    Description

    Goods receipt only. Purchase order of yours on this ticket, or null to unlink.

  • Name
    date
    Type
    string => ISODate
    Description

    Date of the document.

  • Name
    amount
    Type
    number
    Description

    From 0 to 1 000 000 000, or null to clear it.

Responses

  • 200 — returns { "purchase": { ... } }, unchanged if nothing differed
  • 403 — your account is not the buyer of the document
  • 400, 404, 409, 422, 502, 504 — see Purchase documents

Request

PATCH
/api/v3.0/tickets/:ticket_id/purchases/:purchase_id
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/api/v3.0/tickets/:ticket_id/purchases/:purchase_id

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 — deleted
  • 403 — your account is not the buyer of the document
  • 400, 404, 409, 502, 504 — see Purchase documents

Request

DELETE
/api/v3.0/tickets/:ticket_id/purchases/:purchase_id
curl -X DELETE https://api.bob-desk.com/api/v3.0/tickets/:ticket_id/purchases/:purchase_id \
  -H "Authorization: Bearer {token}"

POST/api/v3.0/tickets/:ticket_id/purchases/:purchase_id/document

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": { ... } } with document_id set
  • 400 — no file sent (purchase_file_required)
  • 413 — file larger than 20 MB
  • 415 — file is not a PDF, PNG or JPEG
  • 403, 404, 409, 502, 504 — see Purchase documents

Request

POST
/api/v3.0/tickets/:ticket_id/purchases/:purchase_id/document
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"

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.

  1. Find the validated quotes. Every day, call GET /api/v3.0/tickets?status=validated and keep the quotes with validated: true. Deduplicate on quotes[]._id: one quote must raise only one purchase request. validated is a transient status (it ends as soon as the work is scheduled), so also run a weekly catch-up with GET /api/v3.0/tickets?updated_after={7 days ago, in milliseconds} and read quotes[].validated_at. Never call updated_after alone with a date far in the past.
  2. Choose the requester. Raise the purchase request on behalf of the ticket's validated_by when it is set, otherwise of its creator. A contractor user does not exist in your purchasing system: the person on your side who validated the ticket carries the request.
  3. Get the quote file. Get a ticket returns a download_url on each quote. From the list, pass quotes[].s3_key to Download link of a document. The signed links are served from documents.bob-desk.com: allow this host in your outgoing network rules.
  4. Match the supplier. Store your supplier number in a custom field of the contractors and read it from fields on Contractors.
  5. 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 with purchase_order_id. Creation is idempotent: sending the same document again is safe.
  6. Follow the changes. Subscribe to the ticket.purchase_added webhook to be notified of each new purchase document.