Production API Documentation

accfarm API Docs

Manage API Keys OpenAPI JSON Wallet Support API Metadata
Base URLhttps://accfarm.org/api/v1 Versionv1
AuthHMAC-SHA256 Timestamp Window5 minutes

Introduction

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

  1. Create one API key per integration from API Keys.
  2. Keep signing on the server only. Never expose the secret in frontend code.
  3. Read /catalog, refresh the selected product with /products.php?id=X, then create the order.
  4. Always send a stable external_order_id and Idempotency-Key on POST /orders.php.
  5. Use /orders.php?id=X or ?external_order_id=X to reconcile after retries or timeouts.

Authentication and Request Model

HeaderRequiredDescription
X-API-KeyYesPublic key identifier.
X-TimestampYesUnix timestamp in seconds. Must be within 5 minutes.
X-NonceYesUnique random string, used once per key to block replay.
X-SignatureYesLowercase hex HMAC-SHA256 over the canonical request.
Idempotency-KeyPOST /orders.phpRequired 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

MethodPathDescription
GET/api/v1/API metadata and supported webhook events.
GET/api/v1/catalog.phpList active products with pagination and filters.
GET/api/v1/catalog.php?product_id=XReturn one active product by ID.
GET/api/v1/products.php?id=XGet full product details before checkout.
GET/api/v1/balance.phpRead current wallet balance and pending balance.
POST/api/v1/orders.phpCreate and charge an order using your accfarm wallet.
GET/api/v1/orders.php?id=XRead an order by accfarm order ID.
GET/api/v1/orders.php?external_order_id=XRecover an order using your own external reference.
PUT/api/v1/stock.phpUpdate your product's stock (sellers).
GET/POST/DELETE/api/v1/webhooks.phpView, 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.deleted
  • product.stock_changed, product.price_changed
  • order.created, order.delivered, order.completed
  • order.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 codeHTTPMeaning
missing_auth401One or more required auth headers are missing.
request_expired401Timestamp is too old or too far from server time.
invalid_signature401The canonical string or secret does not match the request.
account_suspended403The account tied to this key is suspended.
missing_idempotency_key400POST /orders.php was sent without Idempotency-Key.
request_in_progress409The same purchase is already being processed.
idempotency_conflict409The same key (or nonce) was reused with a different payload.
insufficient_balance402Wallet balance is too low for the purchase.
product_unavailable404Product is inactive, missing, or seller is not active.
insufficient_stock409Auto-delivery stock is not sufficient right now.
rate_limited429Too many requests for this key in the current window.