curl --request POST \
--url https://api.arcuserp.com/v1/periods/{id}/withdraw-close-request \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"reason": "Raised the close request by mistake while reviewing the period."
}
'import requests
url = "https://api.arcuserp.com/v1/periods/{id}/withdraw-close-request"
payload = { "reason": "Raised the close request by mistake while reviewing the period." }
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: 'Raised the close request by mistake while reviewing the period.'})
};
fetch('https://api.arcuserp.com/v1/periods/{id}/withdraw-close-request', 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/periods/{id}/withdraw-close-request",
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' => 'Raised the close request by mistake while reviewing the period.'
]),
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/periods/{id}/withdraw-close-request"
payload := strings.NewReader("{\n \"reason\": \"Raised the close request by mistake while reviewing the period.\"\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/periods/{id}/withdraw-close-request")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"reason\": \"Raised the close request by mistake while reviewing the period.\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.arcuserp.com/v1/periods/{id}/withdraw-close-request")
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\": \"Raised the close request by mistake while reviewing the period.\"\n}"
response = http.request(request)
puts response.read_body{
"data": {
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"entity_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"period_name": "<string>",
"start_date": "2023-12-25",
"end_date": "2023-12-25",
"status": "open",
"closed_by": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"closed_at": "2023-11-07T05:31:56Z",
"close_requested_by": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"close_requested_at": "2023-11-07T05:31:56Z",
"close_approved_by": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"close_approved_at": "2023-11-07T05:31:56Z",
"reopen_reason": "<string>",
"reopened_by": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"reopened_at": "2023-11-07T05:31:56Z",
"created_at": "2023-11-07T05:31:56Z"
}
}{
"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"
}{
"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"
}{
"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"
}{
"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"
}{
"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"
}{
"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"
}Withdraw a pending period-close request
accounting:close_periodWithdraws a period-close request that has been raised but not yet
approved, returning the period from pending_approval to open.
BOOKS-100 PERIOD-CLOSE-UNDO (2026-08-28). Before this endpoint the
only exits from pending_approval were (a) a second person APPROVING
the request, which closes the period — the opposite of what someone who
mis-clicked wants — or (b) a raw UPDATE. And pending_approval is a
posting-blocking state, so one stray click froze a month of the ledger
with no in-product undo. This is the Rule 25 api-v1 twin of the
Cognito-JWT POST /gl/periods/{id}/withdraw-close-request; both call the
same canonical handler, so authorization and guards are identical.
Authorization, two tiers (Rule 33 point 5 — self-service on your OWN record never requires the org permission):
- Floor —
gl.close_period, the permission that let you RAISE the request in the first place. Gating the route itself ongl.approve_closewould lock the REQUESTER out of taking back their own mis-click, which is the whole population this endpoint exists for. - Plus — if the caller is NOT
close_requested_by, they additionally needgl.approve_close. Cancelling someone else’s request is an approval-authority act. Returns 403only_requester_or_approver_can_withdraw.
Guards:
- Layer 1 isolation — the period is scoped to the API key’s
entity_id; cross-tenant attempts return 404. - State guard — only
pending_approvalperiods can be withdrawn. Returns 409period_not_in_pending_approvalwithcurrent_status. - Row lock — the state read takes
FOR UPDATE, and the UPDATE re-assertsstatus = 'pending_approval', so a concurrentapprove-closecannot slip between the test and the flip. A lost race returns 409period_state_changed. - Audit log (Rule 20) — an
activity_logrow records the prior state, the original requester, the withdrawing user, whether the withdrawer WAS the requester, and the optional reason. - Broadcast (Rule 13) —
gl.period.close_request_withdrawnis emitted, mirroring thegl.period.close_requestedevent it undoes.
Write surface is deliberately three columns and no more: status
back to open, plus close_requested_by and close_requested_at
cleared. close_approved_by / close_approved_at /
close_checksum_hex are NOT touched — on a pending_approval row they
are NULL by construction (approve-close writes them in the SAME
statement that flips status to closed), so writing them would be a
claim about state this handler has not measured.
curl --request POST \
--url https://api.arcuserp.com/v1/periods/{id}/withdraw-close-request \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"reason": "Raised the close request by mistake while reviewing the period."
}
'import requests
url = "https://api.arcuserp.com/v1/periods/{id}/withdraw-close-request"
payload = { "reason": "Raised the close request by mistake while reviewing the period." }
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: 'Raised the close request by mistake while reviewing the period.'})
};
fetch('https://api.arcuserp.com/v1/periods/{id}/withdraw-close-request', 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/periods/{id}/withdraw-close-request",
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' => 'Raised the close request by mistake while reviewing the period.'
]),
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/periods/{id}/withdraw-close-request"
payload := strings.NewReader("{\n \"reason\": \"Raised the close request by mistake while reviewing the period.\"\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/periods/{id}/withdraw-close-request")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"reason\": \"Raised the close request by mistake while reviewing the period.\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.arcuserp.com/v1/periods/{id}/withdraw-close-request")
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\": \"Raised the close request by mistake while reviewing the period.\"\n}"
response = http.request(request)
puts response.read_body{
"data": {
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"entity_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"period_name": "<string>",
"start_date": "2023-12-25",
"end_date": "2023-12-25",
"status": "open",
"closed_by": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"closed_at": "2023-11-07T05:31:56Z",
"close_requested_by": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"close_requested_at": "2023-11-07T05:31:56Z",
"close_approved_by": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"close_approved_at": "2023-11-07T05:31:56Z",
"reopen_reason": "<string>",
"reopened_by": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"reopened_at": "2023-11-07T05:31:56Z",
"created_at": "2023-11-07T05:31:56Z"
}
}{
"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"
}{
"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"
}{
"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"
}{
"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"
}{
"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"
}{
"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
Optional free-text justification. Recorded verbatim in the activity_log description and metadata. No length validation.
"Raised the close request by mistake while reviewing the period."
Response
Close request withdrawn; period returned to open.
An accounting period. Closed periods refuse new journal entry postings (Rule A3). Year-end closing entries that roll revenue and expense activity into retained earnings are posted via a separate explicit endpoint (POST /v1/accounting_periods/{id}/close-fiscal-year). The monthly-period close does NOT by itself post a retained-earnings rollover JE. Verified against live dev RDS accounting.accounting_periods.
Show child attributes
Show child attributes
Was this page helpful?

