# Update Order

> Update an existing order. This is a **partial update** — only the fields you include in the body are changed; everything else stays as-is. All the fields from [Create Order](https://developer.ghn.vn/en/docs/order/create.md#parameters) are accepted; the table below lists the ones with update-specific behavior.

Shares the request contract with [order/create](https://developer.ghn.vn/en/docs/order/create.md)

## Endpoint

| Environment | URL |
| --- | --- |
| Production | `https://online-gateway.ghn.vn/shiip/public-api/v2/shipping-order/update` |
| Staging | `https://dev-online-gateway.ghn.vn/shiip/public-api/v2/shipping-order/update` |

**Method**: `POST`

## Headers

| Header | Required | Description |
| --- | --- | --- |
| `Content-Type` | Yes | `application/json` |
| `Token` | Yes | Shop token (issued by GHN) — [get a token](https://developer.ghn.vn/en/docs/token/get-token.md) |
| `ShopId` | Yes | Shop ID (int) |

## Parameters

Send `order_code` plus only the fields you want to change. **Every [Create Order](https://developer.ghn.vn/en/docs/order/create.md#parameters) field is accepted here with the same meaning** — include it to change it, omit it to leave it unchanged. Only the fields below are specific to Update; see Create Order for all the rest.

- **order_code** (String): GHN tracking code of the order to update. Must belong to your account
- **cod_amount** (Int): New COD amount (VND). Only applied when different from the current value. **Changing it requires `otp`** (see below)
- **otp** (String): One-time code, **required only when changing `cod_amount`**. GHN sends it to your account's registered phone. A wrong/missing code fails with `OTP_NOT_VALID`

## Which fields you can update, by status

A field is only changed if the order's current status allows it. `ready_to_pick` allows everything; the set shrinks as the order moves through the network; delivered / returned / cancelled and other final states allow no changes. Shorthands: **sender info** covers `from_*`, **receiver info** covers `to_*` (name/phone/address), **return info** covers `return_*`, **service** covers `service_id` / `service_type_id`.

| Order status | Fields you can update |
| --- | --- |
| `ready_to_pick` | All fields |
| `picking` | Everything except sender info, `pick_shift`, `pickup_time`, `pick_station_id` |
| `money_collect_picking` | `order_value`, `to_name`/`to_phone`, `weight`, `content`, `required_note`, `note`, `cod_amount`, `cod_failed_amount`, return info, `coupon`, `items` |
| `picked` | service, receiver info, `weight`, `content`, `required_note`, `note`, `cod_amount`, `cod_failed_amount`, return info, `coupon`, `items`, `document_return`, `double_check` |
| `storing`, `transporting`, `sorting` | service, receiver info, `content`, `required_note`, `note`, `cod_amount`, `cod_failed_amount`, return info, `coupon`, `document_return`, `double_check` |
| `delivering` | service, receiver info, `required_note`, `note`, `cod_amount`, `cod_failed_amount`, return info, `document_return`, `double_check` |
| `delivery_fail` | service, receiver info, `content`, `required_note`, `note`, `cod_amount`, return info, `document_return`, `double_check` |
| `waiting_to_return` | service, receiver info, `content`, `required_note`, `note`, `cod_amount`, `cod_failed_amount`, return info, `document_return`, `double_check` |
| `return`, `return_transporting`, `return_sorting` | return contact only (`return_name`, `return_phone`) |
| `return_fail` | return info (`return_name`, `return_phone`, `return_address`) |
| `delivered`, `money_collect_delivering`, `returning`, `returned`, `cancel`, `exception`, `lost`, `damage` | None — the order can no longer be edited |

## Request Example

```bash
curl -X POST https://dev-online-gateway.ghn.vn/shiip/public-api/v2/shipping-order/update \
  -H "Token: 5c1d8a9e-2f4b-11ed-..." \
  -H "ShopId: 92837" \
  -H "Content-Type: application/json" \
  -d '{
    "order_code": "LADAK9",
    "note": "Deliver during office hours",
    "weight": 700
  }'
```

```bash
curl -X POST https://dev-online-gateway.ghn.vn/shiip/public-api/v2/shipping-order/update \
  -H "Token: 5c1d8a9e-2f4b-11ed-..." \
  -H "ShopId: 92837" \
  -H "Content-Type: application/json" \
  -d '{
    "order_code": "LADAK9",
    "cod_amount": 300000,
    "otp": "123456"
  }'
```

## Response

### Success (HTTP 200)

_Captured from a real Staging-environment call._

```json
{
  "code": 200,
  "message": "Success",
  "data": null
}
```

### Response Fields

| Field | Type | Description |
| --- | --- | --- |
| `code` | Int | HTTP code (`200` on success) |
| `message` | String | Result message (`Success`) |
| `data` | null | Always `null` for this endpoint |

### Error — changing COD without a valid OTP (HTTP 400)

_Captured from a real Staging-environment call (`cod_amount` changed, no `otp`)._

```json
{
  "code": 400,
  "message": "Check OTP fail: Lỗi gọi API: notify_check_sms_otp - Key: 'myRequest.OTP' Error:Field validation for 'OTP' failed on the 'required' tag",
  "data": null,
  "code_message": "OTP_NOT_VALID",
  "code_message_value": "Mã OTP đã hết hạn hoặc không đúng."
}
```

## Error Codes

| code_message | HTTP | When |
| --- | --- | --- |
| `USER_ERR_COMMON` | 400 | Missing `order_code` or invalid input |
| `OTP_NOT_VALID` | 400 | `cod_amount` changed with a missing/wrong/expired `otp` |
| `PERMISSION_DENIED_EDIT_COD` | 400 | A staff account without the "Edit COD" permission tried to change COD |
| `PHONE_INVALID` | 400 | A sender/receiver/return phone is malformed |
| `PICKUP_TIME_ERROR` | 400 | Requested pickup time is in the past |
| `CLIENT_NOT_OWNER_OF_SHOP` | 400 | The order does not belong to your account |
| `SERVER_ERROR_COMMON` | 500 | Internal error |
