popbob logo icon
Shop API

Complete an order

Complete delivery and upload proof images.

Completes an order after delivery. The API key must have theorders:complete permission.

Endpoint

POST https://popbob.gg/api/v1/orders/{order_id}/complete

Replace {order_id} with an order belonging to the shop connected to the API key. Completing an order is permanent.

Fields and data formats

FieldLocationRequiredFormat and rules
order_idURL pathYesThe Popbob order ID. The order must exist, belong to the key's shop, and not already be completed.
AuthorizationHTTP headerYesBearer pb_live_.... The API key must includeorders:complete.
Content-TypeHTTP headerYesmultipart/form-data with a boundary generated by your HTTP client.
coordsForm fieldOnly for specified-coordinate ordersA JSON-encoded array of exactly three finite numbers, such as [100,64,-200]. For specified-coordinate orders, the submitted coordinates must be within 10,000 total blocks of the requested coordinates.
noteForm fieldNoPlain text, trimmed before storage, with a maximum of 2,000 characters. Send an empty string or omit the field when no note is needed.
imagesRepeated form fieldYes, one or moreA binary image file part. Repeat the same field name for every proof image. The uploaded bytes must be a valid image that Popbob can process. Send up to 5 images, at most 10 MB each and 25 MB in total.

Only the documented fields are part of the API contract. Do not rely on additional multipart fields being stored or processed.

Multipart request

Send multipart/form-data. Do not set the multipart boundary yourself when using a client library; let the library set it.

coords is optional unless the order uses specified coordinates. When supplied, it must be a JSON array containing exactly three numbers. note is optional and is limited to 2,000 characters. Send each proof image in a separateimages field. At least one proof image is required.

What Popbob receives

The request contains the actual binary bytes for each image. This is the multipart HTTP shape for two images; the boundary value is generated by your HTTP client.

POST /api/v1/orders/order_123/complete HTTP/1.1
Authorization: Bearer pb_live_your_key_here
Content-Type: multipart/form-data; boundary=PopbobBoundary

--PopbobBoundary
Content-Disposition: form-data; name="note"

Delivered successfully.
--PopbobBoundary
Content-Disposition: form-data; name="images"; filename="proof-one.png"
Content-Type: image/png

<binary bytes of proof-one.png>
--PopbobBoundary
Content-Disposition: form-data; name="images"; filename="proof-two.png"
Content-Type: image/png

<binary bytes of proof-two.png>
--PopbobBoundary--

Browser JavaScript

Get real File objects from a file input, then append those objects. The browser sends their binary data.

const input = document.querySelector('#proof-images')
const files = Array.from(input.files)

const form = new FormData()
form.set('coords', JSON.stringify([100, 64, -200]))
form.set('note', 'Delivered successfully.')
for (const file of files) form.append('images', file, file.name)

const response = await fetch(
  'https://popbob.gg/api/v1/orders/order_123/complete',
  {
    method: 'POST',
    headers: {
      Authorization: 'Bearer pb_live_your_key_here',
    },
    body: form,
  }
)

Node.js JavaScript

Read the image bytes from disk, wrap them in a Blob, and append that blob to the form.

const { readFile } = require('node:fs/promises')

const firstImage = await readFile('/absolute/path/proof-one.png')
const secondImage = await readFile('/absolute/path/proof-two.png')
const form = new FormData()
form.set('note', 'Delivered successfully.')
form.append('images', new Blob([firstImage], { type: 'image/png' }), 'proof-one.png')
form.append('images', new Blob([secondImage], { type: 'image/png' }), 'proof-two.png')

const response = await fetch(
  'https://popbob.gg/api/v1/orders/order_123/complete',
  {
    method: 'POST',
    headers: { Authorization: 'Bearer pb_live_your_key_here' },
    body: form,
  }
)

Do not JSON-stringify the files or convert them to base64. When using FormData, do not manually setContent-Type; the runtime adds the multipart boundary.

Successful response

A successful completion returns 200:

{
  "message": "Order completed successfully",
  "vendorEarningsCents": 1250
}

vendorEarningsCents is an integer amount in cents. The response is sent only after Popbob has atomically marked the order complete and updated shop completion statistics.

Response fieldTypeMeaning
messageStringAlways Order completed successfully on success.
vendorEarningsCentsIntegerThe order's seller payout in cents.

Delivery proof images

You can attach one or more proof images by repeating theimages form field. Use clear delivery evidence that lets the customer and shop verify the completed order. The API accepts up to 5 images, with a 10 MB limit per image and a 25 MB combined image limit.

Errors

400 is returned for malformed coordinates, a note over 2,000 characters, missing or unsupported proof images, or an order that is already completed. 403 is returned when the order belongs to another shop. 404 is returned when the order does not exist.

StatusExample errorWhen it happens
400Coords must be a JSON array.The coords form value is not valid JSON.
400Coords required for this orderA specified-coordinate order was completed without coordinates.
400At least one proof image is required.No image file was sent in an images part.
413Request body is too large.The multipart request exceeds the upload request limit.
415Content-Type must be multipart/form-data.The request was not sent as multipart form data.
400Order already completedThe order was already completed by another request or user.
403Forbidden: Order does not belong to this shopThe API key belongs to a different shop.
404Order not foundNo order exists with the supplied ID.

Authentication, permission, and rate-limit responses use the same formats described in the List products reference. After a timeout, fetch the order in the Superpanel before retrying: do not assume a failed client connection means the completion failed.

Still have questions?

If anything is unclear or your question is not answered anywhere in our docs, contact Support. We will help you and update the docs to make the answer clearer.

HelpHelp