Getting started
- Become an approved reseller at reseller.fiddoglow.com and top up your wallet.
- In the portal open Developers and create an API key.
- Call the API from your server. All requests and responses are JSON; money is in USD.
Base URL: https://api.fiddoglow.com/api/v1
Authentication
Send your key in the Authorization header. Keep it on your server — never put it in a web page or mobile app.
curl
curl https://api.fiddoglow.com/api/v1/me \
-H "Authorization: Bearer fg_live_xxxxxxxxxxxxxxxxxxxx"Response
{ "shop_name": "Hodan Beauty", "level": "Gold", "discount_percent": 20, "balance": 150.00, "currency": "USD" }Products
Every active product with your price and live stock. Use it to fill and sync your shop.
GET/products?q=&page=1&limit=100
Response
{
"data": [
{
"id": "cm1x…", "sku": "CJ-STR-118", "name": "Color Juice — Strawberry",
"brand": "Fiddo Glow", "category": "Hair colour", "group": "Color Juice", "variant": "Strawberry",
"image": "https://api.fiddoglow.com/uploads/…jpg",
"in_stock": 42, "retail_price": 12.00, "your_price": 9.60
}
],
"page": 1, "limit": 100, "total": 85, "has_more": false
}GET/products/{id or sku}
GET/delivery
Delivery prices per city (the fee is added to every order).
Price check
Calculates an order without placing it — handy to show the total and check your balance first.
POST/quote
Body
{ "city": "Mogadishu", "items": [ { "sku": "CJ-STR-118", "quantity": 2 } ] }Response
{ "items": [...], "subtotal": 19.20, "delivery_fee": 2.00, "total": 21.20, "balance": 150.00, "enough_balance": true }Create an order
Places the order for your customer and pays it from your wallet straight away. You get back the order with a 6-digit delivery_code — send it to your customer; our rider asks for it on hand-over.
POST/orders
| reference | string | Your own order number. Recommended — sending the same reference again returns the existing order instead of a duplicate (safe retries). You can also use an Idempotency-Key header. |
| customer.name | string | Customer’s name * |
| customer.phone | string | Customer’s phone * |
| customer.city | string | City * (see /delivery) |
| customer.district | string | District |
| customer.address | string | Address / landmark * |
| items[] | array | { product_id | sku, quantity } — up to 100 lines * |
| notes | string | Delivery notes |
curl
curl -X POST https://api.fiddoglow.com/api/v1/orders \
-H "Authorization: Bearer $FG_KEY" -H "Content-Type: application/json" \
-d '{
"reference": "SHOP-1042",
"customer": { "name": "Amina Ali", "phone": "0617778888", "city": "Mogadishu", "address": "Hodan, near KM4" },
"items": [ { "sku": "CJ-STR-118", "quantity": 2 } ]
}'201 Created
{
"data": {
"order_number": "FG-261012-A1B2C3", "reference": "SHOP-1042",
"status": "confirmed", "payment_status": "paid",
"customer": { "name": "Amina Ali", "phone": "0617778888", "city": "Mogadishu", "district": null, "address": "Hodan, near KM4" },
"items": [ { "product_id": "cm1x…", "sku": "CJ-STR-118", "name": "Color Juice — Strawberry", "quantity": 2, "unit_price": 9.60, "total": 19.20 } ],
"subtotal": 19.20, "delivery_fee": 2.00, "total": 21.20, "currency": "USD",
"delivery_code": "482913", "tracking_code": null,
"history": [ { "status": "pending", "note": "Order placed by reseller Hodan Beauty", "at": "…" }, { "status": "confirmed", "note": "…", "at": "…" } ],
"created_at": "…", "updated_at": "…", "delivered_at": null
},
"replayed": false
}If your wallet does not cover the total you get a 400 error and nothing is charged.
List & get orders
GET/orders?status=delivered&updated_since=2026-10-01&page=1&limit=50
GET/orders/{order_number or your reference}
Cancel an order
Possible while the order is still pending or confirmed (before we start preparing it). The full amount goes back to your wallet.
POST/orders/{order_number or reference}/cancel
Body (optional)
{ "reason": "Customer changed their mind" }Wallet
GET/wallet
Response
{ "balance": 128.80, "currency": "USD", "transactions": [ { "type": "order", "amount": -21.20, "balance_after": 128.80, "reference": "FG-261012-A1B2C3", "note": "Order for Amina Ali", "at": "…" } ] }Top-ups are done in the reseller portal (EVC Plus, ZAAD, Sahal, bank or cash).
Order statuses
confirmedPaid from your wallet, waiting to be prepared
processingBeing packed
shippedLeft our store
out_for_deliveryWith the rider
deliveredHanded over with the delivery code
cancelledCancelled — money back in your wallet
returnedCame back — money back in your wallet
Webhooks
Add your URL in Developers. We POST JSON to it for these events, and retry after 30 seconds and 5 minutes if your server does not answer with a 2xx:
order.created·order.status_changed—data.orderis the order object abovewallet.topup_confirmed—data.topupand the newdata.balanceping— the “Send test” button
Request we send
POST https://myshop.com/webhooks/fiddoglow
X-FG-Event: order.status_changed
X-FG-Delivery: evt_8f2c…
X-FG-Signature: t=1760180000,v1=5d41402abc4b2a76b9719d911017c592…
{ "id": "evt_8f2c…", "event": "order.status_changed", "created_at": "…", "data": { "order": { … } } }Always verify the signature: HMAC-SHA256 of "<t>.<raw body>" with your signing secret, compared with v1. Reject requests older than 5 minutes.
Node.js (Express)
app.post('/webhooks/fiddoglow', express.raw({ type: 'application/json' }), (req, res) => {
const [t, v1] = req.get('X-FG-Signature').split(',').map((p) => p.split('=')[1]);
const expected = crypto.createHmac('sha256', process.env.FG_WEBHOOK_SECRET)
.update(`${t}.${req.body}`).digest('hex');
const fresh = Math.abs(Date.now() / 1000 - Number(t)) < 300;
if (!fresh || v1?.length !== expected.length || !crypto.timingSafeEqual(Buffer.from(v1), Buffer.from(expected))) return res.sendStatus(400);
const event = JSON.parse(req.body);
// event.event === 'order.status_changed' → update your order using event.data.order.reference
res.sendStatus(200);
});PHP
$body = file_get_contents('php://input');
parse_str(str_replace(',', '&', $_SERVER['HTTP_X_FG_SIGNATURE']), $sig);
$expected = hash_hmac('sha256', $sig['t'] . '.' . $body, getenv('FG_WEBHOOK_SECRET'));
if (abs(time() - (int)$sig['t']) > 300 || !hash_equals($expected, $sig['v1'])) { http_response_code(400); exit; }
$event = json_decode($body, true);
http_response_code(200);Errors & limits
Errors return a normal HTTP status with a JSON body:
{ "error": "Not enough money in the wallet — please top up first" }- 400 invalid data, not enough stock or balance · 401 missing / wrong key · 403 account paused · 404 not found · 429 too many requests
- Limit: 120 requests per minute per key, 300 new orders per hour. See the RateLimit-Remaining and Retry-After headers.
Questions? WhatsApp us on 0610218518.
