API Imports
This is the part with no equivalent in a payments API, and the part most worth reading. Data you send does not become accounting records by itself. A person accepts it.
The Lifecycle
| Status | Meaning |
|---|---|
pending | Waiting for the merchant. You can still update or delete it. |
imported | In their books. Frozen here. import.local_ref holds the id their copy assigned. |
rejected | The merchant looked at it and declined it. |
Check where your data stands:
curl "https://argorobots.com/v1/revenue?import_status=pending&limit=100" \
-H "Authorization: Bearer ab_..."$url = 'https://argorobots.com/v1/revenue?import_status=pending&limit=100';
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ab_...'],
]);
$page = json_decode(curl_exec($ch), true);
echo count($page['data']), ' still waiting';using var http = new HttpClient();
http.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", "ab_...");
var page = await http.GetFromJsonAsync<JsonElement>(
"https://argorobots.com/v1/revenue?import_status=pending&limit=100");
var waiting = page.GetProperty("data").GetArrayLength();
Console.WriteLine($"{waiting} still waiting");const res = await fetch(
"https://argorobots.com/v1/revenue?import_status=pending&limit=100",
{ headers: { Authorization: "Bearer ab_..." } },
);
const page = await res.json();
console.log(`${page.data.length} still waiting`);import requests
page = requests.get(
"https://argorobots.com/v1/revenue",
params={"import_status": "pending", "limit": 100},
headers={"Authorization": "Bearer ab_..."},
).json()
print(len(page["data"]), "still waiting")Or get the whole picture at once, which is cheaper than seven list calls:
curl https://argorobots.com/v1/account \
-H "Authorization: Bearer ab_..."$ch = curl_init('https://argorobots.com/v1/account');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ab_...'],
]);
$account = json_decode(curl_exec($ch), true);
echo $account['id'];using var http = new HttpClient();
http.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", "ab_...");
var account = await http.GetFromJsonAsync<JsonElement>(
"https://argorobots.com/v1/account");
Console.WriteLine(account.GetProperty("id").GetString());const res = await fetch("https://argorobots.com/v1/account", {
headers: { Authorization: "Bearer ab_..." },
});
const account = await res.json();
console.log(account.id);import requests
account = requests.get(
"https://argorobots.com/v1/account",
headers={"Authorization": "Bearer ab_..."},
).json()
print(account["id"]){
"id": "acct_7a73294c25e3a6de3d4bb998",
"object": "account",
"pending": {
"customer": 3,
"supplier": 0,
"category": 1,
"product": 0,
"expense": 12,
"revenue": 40,
"refund": 2
},
"last_import_at": 1787091029
}
Import Batches
When the merchant approves an import, Argo Books creates a batch that claims every approved object in one transaction. You will normally only read these, but the endpoints are public because seeing them makes your own state easier to reason about.
| Endpoint | Does |
|---|---|
GET /v1/import_batches | List batches, newest first. Filter with status=open|completed|reverted. |
GET /v1/import_batches/<id> | Retrieve one, including per-type counts. |
POST /v1/import_batches | Claim objects. This is what Argo Books calls on approval. |
POST /v1/import_batches/<id>/revert | Release a batch, returning its objects to pending. |
A batch is all-or-nothing. If any object in it is not claimable, the whole batch rolls back with 409 object_not_claimable. A half-imported batch would leave the merchant's books and this queue disagreeing about what was taken, which is a far worse problem than a failed import.
Imports Can Be Undone
The merchant can undo an import in Argo Books like any other action. When they do, the batch is reverted and its objects go back to pending.
So imported is not necessarily permanent, and an object you saw as imported yesterday can legitimately be pending today. If you mirror status on your side, re-read it rather than assuming it only moves forward.
Rejection Is Not Deletion
Two different things, and the difference is who acted:
DELETE /v1/<resource>/<id>is you withdrawing something you sent. Use it when you pushed by mistake.POST /v1/<resource>/<id>/rejectrecords that the merchant saw it and said no.
A rejection is worth surfacing to your user. It usually means your mapping is producing something they do not want, and it is the only signal you get.
Designing Around the Delay
Some practical advice, learned from the shape of the system rather than invented:
- Do not block on import. There is no timeline. A merchant might open Argo Books once a week.
- Set
referenceto your own document number. It is shown to the merchant during review and carried into their books, so it is how a human connects a row in their accounts to a row in your system. - Push referenced objects first. Create the customer, then the revenue that points at it. A dangling reference is rejected immediately.
- Correct with a new object, not an edit. Once something is imported it is frozen, so build for that from the start rather than discovering it at
409. - Use a stable
Idempotency-Key. Deriving it from your own order id means a retry after a timeout cannot create a second copy of a sale.