curl --request POST \
--url https://api.arcuserp.com/v1/purchase-orders/{id}/restore \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"reason": "<string>"
}
'import requests
url = "https://api.arcuserp.com/v1/purchase-orders/{id}/restore"
payload = { "reason": "<string>" }
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({reason: '<string>'})
};
fetch('https://api.arcuserp.com/v1/purchase-orders/{id}/restore', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.arcuserp.com/v1/purchase-orders/{id}/restore",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'reason' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.arcuserp.com/v1/purchase-orders/{id}/restore"
payload := strings.NewReader("{\n \"reason\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.arcuserp.com/v1/purchase-orders/{id}/restore")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"reason\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.arcuserp.com/v1/purchase-orders/{id}/restore")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"reason\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"object": "purchase_order",
"entity_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"po_number": "<string>",
"vendor_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"location_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"status": "draft",
"po_date": "2023-11-07T05:31:56Z",
"expected_date": "2023-11-07T05:31:56Z",
"subtotal": 123,
"tax_total": 123,
"shipping_total": 123,
"po_total": 123,
"currency": "USD",
"notes": "<string>",
"internal_notes": "<string>",
"metadata": {},
"created_at": "2023-11-07T05:31:56Z",
"updated_at": "2023-11-07T05:31:56Z",
"approved_total": 123,
"approval_stale": true,
"header_editability": {},
"billed_total": 123,
"unbilled_balance": 123,
"billed_by_bill": [
{
"bill_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"bill_number": "<string>",
"goods_on_po": 123,
"goods_total": 123,
"status": "<string>"
}
],
"landed_bills": [
{
"bill_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"bill_number": "<string>",
"vendor_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"vendor_name": "<string>",
"bill_date": "2023-12-25",
"status": "<string>",
"mode": "case_a",
"categories": [
"<string>"
],
"allocated_on_this_po": 123
}
]
}{
"error": "not_found",
"code": "not_found",
"type": "not_found",
"hint": "The requested order does not exist or does not belong to this entity.",
"param": "expand[0]",
"required": "accounts:read",
"request_id": "req_abc123"
}Restore a cancelled purchase order
Restores a cancelled purchase order to open: the exact inverse of the cancel. The order’s status returns to open, its cancellation stamps are cleared, the balance the cancel zeroed is written back, its units count as on order again, and the approval threshold is asked again. Only an order that was open when it was cancelled can be restored; an order with a receipt, a bill that is not voided, an open vendor prepayment, or a drop-ship order is refused by name, and a refusal changes nothing. No stock, no journal entry and no money moves. Requires purchasing:write scope, and the API key’s owning user must also hold the purchasing.create permission. A repeat sent with the same Idempotency-Key replays the first answer; a repeat without one, or with a new one, answers 409 po_not_cancelled.
curl --request POST \
--url https://api.arcuserp.com/v1/purchase-orders/{id}/restore \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"reason": "<string>"
}
'import requests
url = "https://api.arcuserp.com/v1/purchase-orders/{id}/restore"
payload = { "reason": "<string>" }
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({reason: '<string>'})
};
fetch('https://api.arcuserp.com/v1/purchase-orders/{id}/restore', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.arcuserp.com/v1/purchase-orders/{id}/restore",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'reason' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.arcuserp.com/v1/purchase-orders/{id}/restore"
payload := strings.NewReader("{\n \"reason\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.arcuserp.com/v1/purchase-orders/{id}/restore")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"reason\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.arcuserp.com/v1/purchase-orders/{id}/restore")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"reason\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"object": "purchase_order",
"entity_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"po_number": "<string>",
"vendor_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"location_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"status": "draft",
"po_date": "2023-11-07T05:31:56Z",
"expected_date": "2023-11-07T05:31:56Z",
"subtotal": 123,
"tax_total": 123,
"shipping_total": 123,
"po_total": 123,
"currency": "USD",
"notes": "<string>",
"internal_notes": "<string>",
"metadata": {},
"created_at": "2023-11-07T05:31:56Z",
"updated_at": "2023-11-07T05:31:56Z",
"approved_total": 123,
"approval_stale": true,
"header_editability": {},
"billed_total": 123,
"unbilled_balance": 123,
"billed_by_bill": [
{
"bill_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"bill_number": "<string>",
"goods_on_po": 123,
"goods_total": 123,
"status": "<string>"
}
],
"landed_bills": [
{
"bill_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"bill_number": "<string>",
"vendor_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"vendor_name": "<string>",
"bill_date": "2023-12-25",
"status": "<string>",
"mode": "case_a",
"categories": [
"<string>"
],
"allocated_on_this_po": 123
}
]
}{
"error": "not_found",
"code": "not_found",
"type": "not_found",
"hint": "The requested order does not exist or does not belong to this entity.",
"param": "expand[0]",
"required": "accounts:read",
"request_id": "req_abc123"
}Authorizations
API key issued per entity via Settings > Developers > API Keys.
Each key carries scopes (e.g. orders:read, products:write).
Bearer token format: Authorization: Bearer ark_live_ent_Test keys use ark_test_ent_. Both are issued per entity
via Settings > Developers > API Keys.
Path Parameters
Body
Why the purchase order is being restored (3 to 500 characters), recorded on its status history and its activity.
3 - 500Response
The restored purchase order, as GET /purchase-orders/{id} returns it.
A purchase order issued to a vendor.
purchase_order draft, approved, sent, partially_received, received, closed, cancelled The order total as it stood when an approver approved this purchase order -- the basis they actually saw. NULL on every approval predating 2026-08-17 (no backfill) and NULL means "basis unknown", which is treated as fail-safe.
DERIVED, not stored. True when the order is approved AND its total has since risen above approved_total. The purchase order still receives normally; this is a non-blocking prompt to re-approve.
Per-field verdicts for the header fields, from the same predicate the write handlers enforce. Keys: notes, internal_notes, due_date, expected_date, location_id, po_tracking_reference. Each is { allowed: boolean, reason: string|null } where reason is a token from the PoLineEditReason vocabulary.
Show child attributes
Show child attributes
How much of this purchase order's GOODS a vendor bill has already taken over: the sum of the goods lines that count toward this purchase order on every bill in open, partial, paid or written_off, never a bill's header total and never a landed, freight or expense line on it. A line counts toward this purchase order when its po_item_id names one of its lines, or when it names none and the bill's po_id is this purchase order (one bill may bill several purchase orders, and each is billed only its own lines). 0 when no bill has been raised, never null.
What the purchase order STILL commits the buyer to, compared like with like: 0 once every ordered goods unit is billed (where the units can be seen; goods money otherwise), else max(order_total - billed_total, 0), cents-quantized. A bill line's units count under the product of the purchase-order line its po_item_id names, and under the bill line's own product only when it names none, so a keyed line with no product of its own still counts. Read this rather than balance_due, which on a purchase order is the ORIGINAL COMMITMENT the header was raised for and is never decremented when a bill is raised (the payable is the bill).
Per bill whose goods lines count toward this purchase order (the lines billed_total sums, here for every bill status): goods_on_po, the goods it bills on this purchase order, and goods_total, the goods it carries in all. A bill that also bills another purchase order shows goods_on_po below goods_total; on a bill of this purchase order alone the two are equal. Each entry carries the bill's status, so a reader that names the bills that billed these goods can leave a voided bill out. An empty array when no bill bills it, never null.
Show child attributes
Show child attributes
The landed bills that reach this purchase order (the Linked Documents card): a Case-A bill, a landed-only bill naming the PO, or a receipt-set bill on its receipts. Identity only (no amounts); the allocations are getPurchaseOrderLandedCostsV1's.
Show child attributes
Show child attributes
Was this page helpful?

