accfarm API Docs
Manage API Keys OpenAPI JSON Wallet Support API MetadataIntroduction
The accfarm Partner API lets sellers and buyers integrate programmatically - browse the live catalog, place bulk orders, check your wallet balance, keep your own stock in sync, and receive real-time webhook notifications when things change. Every request is authenticated with HMAC-SHA256 signing (see Authentication below) and every purchase goes through the exact same code path as a normal website order, so there's no separate, less-safe way to move money through the API.
Quick Start
- Create one API key per integration from API Keys.
- Keep signing on the server only. Never expose the secret in frontend code.
- Read
/catalog, refresh the selected product with/products.php?id=X, then create the order. - Always send a stable
external_order_idandIdempotency-Keyon POST /orders.php. - Use
/orders.php?id=Xor?external_order_id=Xto reconcile after retries or timeouts.
Authentication and Request Model
| Header | Required | Description |
|---|---|---|
X-API-Key | Yes | Public key identifier. |
X-Timestamp | Yes | Unix timestamp in seconds. Must be within 5 minutes. |
X-Nonce | Yes | Unique random string, used once per key to block replay. |
X-Signature | Yes | Lowercase hex HMAC-SHA256 over the canonical request. |
Idempotency-Key | POST /orders.php | Required on order creation. Reuse it when retrying the same purchase. |
Canonical String
METHOD PATH QUERY TIMESTAMP NONCE BODY_HASH
- PATH must match the exact request path, including /api/v1.
- QUERY is the raw query string without ?, in the same order you send.
- BODY_HASH is empty for GET and SHA256 of the exact raw JSON body for POST.
- X-Signature must be lowercase hex HMAC-SHA256.
Endpoints
| Method | Path | Description |
|---|---|---|
GET | /api/v1/ | API metadata and supported webhook events. |
GET | /api/v1/catalog.php | List active products with pagination and filters. |
GET | /api/v1/catalog.php?product_id=X | Return one active product by ID. |
GET | /api/v1/products.php?id=X | Get full product details before checkout. |
GET | /api/v1/balance.php | Read current wallet balance and pending balance. |
POST | /api/v1/orders.php | Create and charge an order using your accfarm wallet. |
GET | /api/v1/orders.php?id=X | Read an order by accfarm order ID. |
GET | /api/v1/orders.php?external_order_id=X | Recover an order using your own external reference. |
PUT | /api/v1/stock.php | Update your product's stock (sellers). |
GET/POST/DELETE | /api/v1/webhooks.php | View, configure, or remove your webhook subscription. |
Example - PHP
Create an order:
$apiKey = 'your_api_key';
$apiSecret = 'your_api_secret';
$method = 'POST';
$path = '/api/v1/orders.php';
$query = '';
$body = json_encode([
'product_id' => 42,
'quantity' => 1,
'external_order_id' => 'shop-order-100045',
]);
$timestamp = (string) time();
$nonce = bin2hex(random_bytes(16));
$bodyHash = hash('sha256', $body);
$canonical = implode("\n", [$method, $path, $query, $timestamp, $nonce, $bodyHash]);
$signature = hash_hmac('sha256', $canonical, $apiSecret);
$ch = curl_init('https://accfarm.org' . $path);
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'X-API-Key: ' . $apiKey,
'X-Timestamp: ' . $timestamp,
'X-Nonce: ' . $nonce,
'X-Signature: ' . $signature,
'Content-Type: application/json',
'Idempotency-Key: idem-shop-order-100045',
],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
Example response:
{
"success": true,
"data": {
"id": 16591,
"order_number": "ORD-CFFD69-16591",
"external_order_id": "shop-order-100045",
"status": "delivered",
"quantity": 1,
"total_amount": 0.59,
"currency": "USD",
"delivery": {
"available": true,
"items": ["user@example.com:password123"]
},
"links": {
"web_url": "https://accfarm.org/orders?view=16591",
"api_url": "https://accfarm.org/api/v1/orders.php?id=16591"
}
}
}
Webhooks
Configure a webhook per key from API Keys using POST /api/v1/webhooks.php. Each key has its own URL, secret, and subscribed events.
Delivery headers
X-AccFarm-Event-Id- stable event ID.X-AccFarm-Delivery-Id- unique delivery attempt ID.X-AccFarm-Webhook-Event- event name.X-AccFarm-Webhook-Timestamp- Unix timestamp.X-AccFarm-Signature-Version- currently v1.X-AccFarm-Webhook-Signature- lowercase hex HMAC-SHA256.
Delivery rules
- Only public https:// endpoints are allowed - localhost and private IP destinations are rejected.
- Any 2xx response marks a delivery successful.
- Non-2xx responses and network failures are retried with exponential backoff, up to 10 attempts.
- Your receiver should be idempotent by
X-AccFarm-Delivery-Id.
Supported events
product.created,product.updated,product.deletedproduct.stock_changed,product.price_changedorder.created,order.delivered,order.completedorder.refunded,order.disputed
Note: order.* events are only delivered to a subscription owned by the buyer or seller involved in that specific order - never broadcast to every subscriber.
Verifying a webhook
$webhookSecret = 'whsec_...';
$timestamp = $_SERVER['HTTP_X_ACCFARM_WEBHOOK_TIMESTAMP'] ?? '';
$deliveryId = $_SERVER['HTTP_X_ACCFARM_DELIVERY_ID'] ?? '';
$eventId = $_SERVER['HTTP_X_ACCFARM_EVENT_ID'] ?? '';
$eventType = $_SERVER['HTTP_X_ACCFARM_WEBHOOK_EVENT'] ?? '';
$signature = strtolower($_SERVER['HTTP_X_ACCFARM_WEBHOOK_SIGNATURE'] ?? '');
$rawBody = file_get_contents('php://input') ?: '';
$bodyHash = hash('sha256', $rawBody);
$canonical = implode("\n", [$timestamp, $deliveryId, $eventId, $eventType, $bodyHash]);
$expected = hash_hmac('sha256', $canonical, $webhookSecret);
if (!hash_equals($expected, $signature)) {
http_response_code(401);
exit('Invalid webhook signature');
}
Errors and Rate Limits
{
"success": false,
"error": {
"code": "invalid_signature",
"message": "Signature does not match the canonical request.",
"status": 401
}
}
Rate limit headers
X-RateLimit-Limit- maximum requests per minute for the key.X-RateLimit-Remaining- remaining requests in the current window.X-RateLimit-Reset- Unix timestamp when the current window resets.Retry-After- returned only on 429.
| Error code | HTTP | Meaning |
|---|---|---|
missing_auth | 401 | One or more required auth headers are missing. |
request_expired | 401 | Timestamp is too old or too far from server time. |
invalid_signature | 401 | The canonical string or secret does not match the request. |
account_suspended | 403 | The account tied to this key is suspended. |
missing_idempotency_key | 400 | POST /orders.php was sent without Idempotency-Key. |
request_in_progress | 409 | The same purchase is already being processed. |
idempotency_conflict | 409 | The same key (or nonce) was reused with a different payload. |
insufficient_balance | 402 | Wallet balance is too low for the purchase. |
product_unavailable | 404 | Product is inactive, missing, or seller is not active. |
insufficient_stock | 409 | Auto-delivery stock is not sufficient right now. |
rate_limited | 429 | Too many requests for this key in the current window. |