# Create an order

`POST /v2/orders`

**Base URLs:**

- Sandbox: `https://api.sandbox.bobgo.co.za`
- Production: `https://api.bobgo.co.za`

**Authentication:** Bearer token required (see the Authentication page).

## Example request

```bash
curl -X POST 'https://api.sandbox.bobgo.co.za/v2/orders' \
  -H "content-type: application/json" \
  -H "Authorization: Bearer <API_KEY>" \
  -d '{
  "channel_order_number": "ABC123",
  "customer_name": "John",
  "customer_surname": "Doe",
  "customer_email": "john@doe.com",
  "customer_phone": "0123456789",
  "currency": "ZAR",
  "buyer_selected_shipping_cost": 49.99,
  "buyer_selected_shipping_method": "Standard Shipping",
  "delivery_address": {
    "company": "",
    "street_address": "171 Dallas Ave",
    "local_area": "Menlyn",
    "city": "Pretoria",
    "zone": "Gauteng",
    "country": "ZA",
    "code": "0181"
  },
  "order_items": [
    {
      "description": "item1",
      "vendor": "item1",
      "sku": "sku1",
      "unit_price": 100,
      "qty": 15,
      "unit_weight_kg": 0.1,
      "unit_length_cm": 20,
      "unit_width_cm": 15,
      "unit_height_cm": 5,
      "channel_image_url": "https://example.com/images/sku1.jpg"
    },
    {
      "description": "item2",
      "vendor": "item2",
      "sku": "sku2",
      "unit_price": 200,
      "qty": 10,
      "unit_weight_kg": 0.1
    }
  ],
  "payment_status": "paid"
}'
```

Creates an order on your Bob Go account.

**For an API key, only a non-empty `order_items` array is enforced**, plus
`channel_order_number` when the order lands on an API channel. Every other
validation failure is logged and ignored, and the order is created anyway. That
is deliberate, so a plugin or cart integration never loses an order over a
field it could not fill, but it means a malformed order comes back `200` rather
than `400`. Check what you sent; the API will not.

Send the rest regardless. The more you send, the more of the platform works
without further calls: a delivery address makes the order rateable, order items
make packing slips useful, and weights make the rate accurate.

The "Required" column below is what the API rejects you for. Everything marked
"send it" is optional to the API and needed in practice.

## Request

### Customer

| Field | Type | Required | Notes |
| --- | --- | --- | --- |
| `customer_name` | string | send it | First name. |
| `customer_surname` | string | send it | |
| `customer_email` | string | send it | Used for tracking notifications. Send this or `customer_phone`. |
| `customer_phone` | string | send it | Used for delivery notifications. Send this or `customer_email`. |

### Order

| Field | Type | Required | Notes |
| --- | --- | --- | --- |
| `channel_order_number` | string | on an API channel | Your own order number. Shows in the dashboard and on documents. Required when the order lands on an API channel, which is always the case with a channel key. On any channel, a second order with the same number is rejected with `Order number already exists on channel.` Left out elsewhere, Bob Go generates one, e.g. `O-RC4R`. |
| `currency` | string | no | Defaults to `ZAR` when omitted. |
| `buyer_selected_shipping_cost` | number | send it | What the buyer paid for shipping. Must be `>= 0`. |
| `buyer_selected_shipping_method` | string | send it | What they picked, e.g. `Standard Shipping`. Send the `service_name` from the checkout rate. |
| `payment_status` | string | send it | `unpaid`, `pending`, `authorized`, `partially-paid`, `paid`, `partially-refunded`, `refunded` or `voided`. Comes back `unpaid` when you leave it out. Not checked for an API key, so send one of these exactly. |

### Delivery address

Optional, and validated only if you send it: an order without one is created
fine, but it cannot be rated or fulfilled until you add one with
[`PATCH /v2/orders`](order_update.md). Send it if you have it.

Same shape as everywhere else, see
[address fields](../4_Important%20concepts/README.md#addresses). When you do
send an address, `street_address`, `local_area`, `city`, `zone`, `country` and
`code` all need to be there for it to geocode well. `company` is optional.

### Order items

`order_items` itself is required and must hold at least one entry. The fields
on each entry are not enforced for an API key, but the limits below are what
the dashboard applies, and a value outside them will be rejected there.

| Field | Type | Required | Notes |
| --- | --- | --- | --- |
| `description` | string | send it | |
| `unit_price` | number | send it | Per unit, in ZAR. Between 0 and 500000. |
| `qty` | integer | send it | Between 0 and 2000. |
| `unit_weight_kg` | number | send it | Per unit. Drives the rate when the order is fulfilled. Between 0 and 2000. |
| `sku` | string | no | |
| `vendor` | string | no | |
| `unit_length_cm` | number | no | Per unit. Matched by rules that look at item or stacked dimensions, for example one that sets the parcels for large items. Send all three dimensions or none: an item missing any of them is left out of the stacked dimensions. |
| `unit_width_cm` | number | no | As `unit_length_cm`. |
| `unit_height_cm` | number | no | As `unit_length_cm`. |
| `channel_image_url` | string | no | A public URL for the product image. See [item images](#item-images). |

### Item images

Send `channel_image_url` on an item and Bob Go downloads the image after the
order is created, keeps its own copy, and shows it against the item in the
dashboard and on packing and picking slips. The URL must be publicly reachable
and return the image with a `200`: Bob Go fetches it with a plain `GET` and no
credentials.

Each item comes back with `image_url`. That is the URL you sent until Bob Go's
copy is ready, and the copy after that. If the download fails, it stays the URL
you sent.

## Response

The response is the created order, including the Bob Go `id`. Keep it, because it is
what [`PATCH /v2/orders`](order_update.md) and
[`GET /v2/order-fulfillments`](order_fulfillments.md) take.
