Get started
Quick start
Make your first call, create a customer, raise an invoice and receive a webhook. It takes about 15 minutes against a staging organization.
You need a staging organization and an API key for it with the scopes customers:write, invoices:write and webhooks:manage. Your organization's admin creates the key: see Set up an integration.
Set your key and base URL#
Every example on this site reads two environment variables, so the same code runs against staging and production and the key never appears in your source.
export FLUX_BASE_URL="https://<your Flux API host>" export FLUX_API_KEY="flx_live_…"On Windows PowerShell:
$env:FLUX_BASE_URL = "https://<your Flux API host>" $env:FLUX_API_KEY = "flx_live_…"Environment API base URL Use Production production API base URL Your organization's live data. Staging https://erpapi.osigla.com.ngconfirm the staging URL can be publishedFor building and testing, with a staging organization. how integrators get staging access Make your first call#
List one customer. A
200means your key works.curl "$FLUX_BASE_URL/api/v1/customers?limit=1" \ -H "Authorization: ApiKey $FLUX_API_KEY"// Node.js 18+ (built-in fetch). Save as an .mjs file. const response = await fetch(`${process.env.FLUX_BASE_URL}/api/v1/customers?limit=1`, { headers: { Authorization: `ApiKey ${process.env.FLUX_API_KEY}`, }, }); console.log(response.status, await response.json());import os import requests response = requests.get( f"{os.environ['FLUX_BASE_URL']}/api/v1/customers", headers={"Authorization": f"ApiKey {os.environ['FLUX_API_KEY']}"}, params={ "limit": 1, }, timeout=30, ) print(response.status_code, response.json())<?php $ch = curl_init(getenv('FLUX_BASE_URL') . '/api/v1/customers?limit=1'); curl_setopt_array($ch, [ CURLOPT_HTTPHEADER => [ 'Authorization: ApiKey ' . getenv('FLUX_API_KEY'), ], CURLOPT_RETURNTRANSFER => true, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_HTTP_CODE), ' ', $response, PHP_EOL;using System.Net.Http.Headers; var baseUrl = Environment.GetEnvironmentVariable("FLUX_BASE_URL"); using var http = new HttpClient(); http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("ApiKey", Environment.GetEnvironmentVariable("FLUX_API_KEY")); using var request = new HttpRequestMessage(HttpMethod.Get, $"{baseUrl}/api/v1/customers?limit=1"); using var response = await http.SendAsync(request); Console.WriteLine($"{(int)response.StatusCode} {await response.Content.ReadAsStringAsync()}");package main import ( "fmt" "io" "net/http" "os" ) func main() { req, err := http.NewRequest("GET", os.Getenv("FLUX_BASE_URL")+"/api/v1/customers?limit=1", nil) if err != nil { panic(err) } req.Header.Set("Authorization", "ApiKey "+os.Getenv("FLUX_API_KEY")) resp, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer resp.Body.Close() out, _ := io.ReadAll(resp.Body) fmt.Println(resp.StatusCode, string(out)) }200 OK
A new staging organization may have no customers yet, so
datacan be empty.{ "data": [], "nextCursor": null, "hasMore": false }A
401 unauthorizedmeans the key is missing, mistyped or revoked. Check the header isAuthorization: ApiKey flx_live_…, with the wordApiKey, notBearer.Create a customer#
Use your own id for the customer as
externalRef, so you can find it again. TheIdempotency-Keymakes the request safe to retry.curl -X POST "$FLUX_BASE_URL/api/v1/customers" \ -H "Authorization: ApiKey $FLUX_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: create-customer-ws_42" \ -d '{ "customerType": "company", "companyName": "Leadway Group", "legalName": "Leadway Group Plc", "email": "finance@leadway.example", "phone": "+2348012345678", "address": "121 Funso Williams Ave, Lagos", "taxId": "12345678-0001", "vatNumber": "VAT-001", "rcNumber": "RC123456", "externalRef": "ws_42", "regulatedEntityCount": 3, "defaultCurrency": "NGN", "contacts": [ { "name": "Finance", "email": "finance@leadway.example", "isBillingPrimary": true, "ccOnInvoices": false }, { "name": "CFO", "email": "cfo@leadway.example", "isBillingPrimary": false, "ccOnInvoices": true } ] }'// Node.js 18+ (built-in fetch). Save as an .mjs file. const response = await fetch(`${process.env.FLUX_BASE_URL}/api/v1/customers`, { method: "POST", headers: { Authorization: `ApiKey ${process.env.FLUX_API_KEY}`, "Content-Type": "application/json", "Idempotency-Key": "create-customer-ws_42", }, body: JSON.stringify({ customerType: "company", companyName: "Leadway Group", legalName: "Leadway Group Plc", email: "finance@leadway.example", phone: "+2348012345678", address: "121 Funso Williams Ave, Lagos", taxId: "12345678-0001", vatNumber: "VAT-001", rcNumber: "RC123456", externalRef: "ws_42", regulatedEntityCount: 3, defaultCurrency: "NGN", contacts: [ { name: "Finance", email: "finance@leadway.example", isBillingPrimary: true, ccOnInvoices: false, }, { name: "CFO", email: "cfo@leadway.example", isBillingPrimary: false, ccOnInvoices: true, }, ], }), }); console.log(response.status, await response.json());import os import requests response = requests.post( f"{os.environ['FLUX_BASE_URL']}/api/v1/customers", headers={ "Authorization": f"ApiKey {os.environ['FLUX_API_KEY']}", "Idempotency-Key": "create-customer-ws_42", }, json={ "customerType": "company", "companyName": "Leadway Group", "legalName": "Leadway Group Plc", "email": "finance@leadway.example", "phone": "+2348012345678", "address": "121 Funso Williams Ave, Lagos", "taxId": "12345678-0001", "vatNumber": "VAT-001", "rcNumber": "RC123456", "externalRef": "ws_42", "regulatedEntityCount": 3, "defaultCurrency": "NGN", "contacts": [ { "name": "Finance", "email": "finance@leadway.example", "isBillingPrimary": True, "ccOnInvoices": False, }, { "name": "CFO", "email": "cfo@leadway.example", "isBillingPrimary": False, "ccOnInvoices": True, }, ], }, timeout=30, ) print(response.status_code, response.json())<?php $body = <<<'JSON' { "customerType": "company", "companyName": "Leadway Group", "legalName": "Leadway Group Plc", "email": "finance@leadway.example", "phone": "+2348012345678", "address": "121 Funso Williams Ave, Lagos", "taxId": "12345678-0001", "vatNumber": "VAT-001", "rcNumber": "RC123456", "externalRef": "ws_42", "regulatedEntityCount": 3, "defaultCurrency": "NGN", "contacts": [ { "name": "Finance", "email": "finance@leadway.example", "isBillingPrimary": true, "ccOnInvoices": false }, { "name": "CFO", "email": "cfo@leadway.example", "isBillingPrimary": false, "ccOnInvoices": true } ] } JSON; $ch = curl_init(getenv('FLUX_BASE_URL') . '/api/v1/customers'); curl_setopt_array($ch, [ CURLOPT_CUSTOMREQUEST => 'POST', CURLOPT_HTTPHEADER => [ 'Authorization: ApiKey ' . getenv('FLUX_API_KEY'), 'Content-Type: application/json', 'Idempotency-Key: create-customer-ws_42', ], CURLOPT_POSTFIELDS => $body, CURLOPT_RETURNTRANSFER => true, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_HTTP_CODE), ' ', $response, PHP_EOL;using System.Net.Http.Headers; using System.Text; var baseUrl = Environment.GetEnvironmentVariable("FLUX_BASE_URL"); using var http = new HttpClient(); http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("ApiKey", Environment.GetEnvironmentVariable("FLUX_API_KEY")); using var request = new HttpRequestMessage(HttpMethod.Post, $"{baseUrl}/api/v1/customers"); request.Headers.Add("Idempotency-Key", "create-customer-ws_42"); request.Content = new StringContent(""" { "customerType": "company", "companyName": "Leadway Group", "legalName": "Leadway Group Plc", "email": "finance@leadway.example", "phone": "+2348012345678", "address": "121 Funso Williams Ave, Lagos", "taxId": "12345678-0001", "vatNumber": "VAT-001", "rcNumber": "RC123456", "externalRef": "ws_42", "regulatedEntityCount": 3, "defaultCurrency": "NGN", "contacts": [ { "name": "Finance", "email": "finance@leadway.example", "isBillingPrimary": true, "ccOnInvoices": false }, { "name": "CFO", "email": "cfo@leadway.example", "isBillingPrimary": false, "ccOnInvoices": true } ] } """, Encoding.UTF8, "application/json"); using var response = await http.SendAsync(request); Console.WriteLine($"{(int)response.StatusCode} {await response.Content.ReadAsStringAsync()}");package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "customerType": "company", "companyName": "Leadway Group", "legalName": "Leadway Group Plc", "email": "finance@leadway.example", "phone": "+2348012345678", "address": "121 Funso Williams Ave, Lagos", "taxId": "12345678-0001", "vatNumber": "VAT-001", "rcNumber": "RC123456", "externalRef": "ws_42", "regulatedEntityCount": 3, "defaultCurrency": "NGN", "contacts": [ { "name": "Finance", "email": "finance@leadway.example", "isBillingPrimary": true, "ccOnInvoices": false }, { "name": "CFO", "email": "cfo@leadway.example", "isBillingPrimary": false, "ccOnInvoices": true } ] }`) req, err := http.NewRequest("POST", os.Getenv("FLUX_BASE_URL")+"/api/v1/customers", body) if err != nil { panic(err) } req.Header.Set("Authorization", "ApiKey "+os.Getenv("FLUX_API_KEY")) req.Header.Set("Content-Type", "application/json") req.Header.Set("Idempotency-Key", "create-customer-ws_42") resp, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer resp.Body.Close() out, _ := io.ReadAll(resp.Body) fmt.Println(resp.StatusCode, string(out)) }201 Created
{ "id": 812, "externalRef": "ws_42", "customerType": "company", "name": "Leadway Group Plc", "legalName": "Leadway Group Plc", "companyName": "Leadway Group", "firstName": null, "lastName": null, "email": "finance@leadway.example", "phone": "+2348012345678", "address": "121 Funso Williams Ave, Lagos", "taxId": "12345678-0001", "vatNumber": "VAT-001", "rcNumber": "RC123456", "parentCustomerId": null, "regulatedEntityCount": 3, "defaultCurrency": "NGN", "defaultSettlementBankId": null, "contacts": [ { "name": "Finance", "email": "finance@leadway.example", "phone": null, "isBillingPrimary": true, "ccOnInvoices": false }, { "name": "CFO", "email": "cfo@leadway.example", "phone": null, "isBillingPrimary": false, "ccOnInvoices": true } ], "createdAt": "2026-11-01T10:20:00", "updatedAt": null }Keep the
id: you need it to raise an invoice.Raise an invoice#
Invoice lines reference services in the organization's catalogue by
serviceId. The API can't list services yet, so ask your organization's admin for the ids. The examples use7and9.curl -X POST "$FLUX_BASE_URL/api/v1/invoices" \ -H "Authorization: ApiKey $FLUX_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: invoice-ws_42-2026-11" \ -d '{ "externalRef": "ws_42-2026-11", "customerId": 812, "currency": "NGN", "dueDate": "2026-11-15", "discount": 0, "periodStart": "2026-11-01", "periodEnd": "2026-11-30", "invoiceType": "subscription", "nextDueDate": "2026-12-01", "comments": "November subscription", "lines": [ { "serviceId": 7, "quantity": 3, "unitPrice": 250000, "detail": "3 entities" }, { "serviceId": 9, "amount": 50000 } ] }'// Node.js 18+ (built-in fetch). Save as an .mjs file. const response = await fetch(`${process.env.FLUX_BASE_URL}/api/v1/invoices`, { method: "POST", headers: { Authorization: `ApiKey ${process.env.FLUX_API_KEY}`, "Content-Type": "application/json", "Idempotency-Key": "invoice-ws_42-2026-11", }, body: JSON.stringify({ externalRef: "ws_42-2026-11", customerId: 812, currency: "NGN", dueDate: "2026-11-15", discount: 0, periodStart: "2026-11-01", periodEnd: "2026-11-30", invoiceType: "subscription", nextDueDate: "2026-12-01", comments: "November subscription", lines: [ { serviceId: 7, quantity: 3, unitPrice: 250000, detail: "3 entities", }, { serviceId: 9, amount: 50000, }, ], }), }); console.log(response.status, await response.json());import os import requests response = requests.post( f"{os.environ['FLUX_BASE_URL']}/api/v1/invoices", headers={ "Authorization": f"ApiKey {os.environ['FLUX_API_KEY']}", "Idempotency-Key": "invoice-ws_42-2026-11", }, json={ "externalRef": "ws_42-2026-11", "customerId": 812, "currency": "NGN", "dueDate": "2026-11-15", "discount": 0, "periodStart": "2026-11-01", "periodEnd": "2026-11-30", "invoiceType": "subscription", "nextDueDate": "2026-12-01", "comments": "November subscription", "lines": [ { "serviceId": 7, "quantity": 3, "unitPrice": 250000, "detail": "3 entities", }, { "serviceId": 9, "amount": 50000, }, ], }, timeout=30, ) print(response.status_code, response.json())<?php $body = <<<'JSON' { "externalRef": "ws_42-2026-11", "customerId": 812, "currency": "NGN", "dueDate": "2026-11-15", "discount": 0, "periodStart": "2026-11-01", "periodEnd": "2026-11-30", "invoiceType": "subscription", "nextDueDate": "2026-12-01", "comments": "November subscription", "lines": [ { "serviceId": 7, "quantity": 3, "unitPrice": 250000, "detail": "3 entities" }, { "serviceId": 9, "amount": 50000 } ] } JSON; $ch = curl_init(getenv('FLUX_BASE_URL') . '/api/v1/invoices'); curl_setopt_array($ch, [ CURLOPT_CUSTOMREQUEST => 'POST', CURLOPT_HTTPHEADER => [ 'Authorization: ApiKey ' . getenv('FLUX_API_KEY'), 'Content-Type: application/json', 'Idempotency-Key: invoice-ws_42-2026-11', ], CURLOPT_POSTFIELDS => $body, CURLOPT_RETURNTRANSFER => true, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_HTTP_CODE), ' ', $response, PHP_EOL;using System.Net.Http.Headers; using System.Text; var baseUrl = Environment.GetEnvironmentVariable("FLUX_BASE_URL"); using var http = new HttpClient(); http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("ApiKey", Environment.GetEnvironmentVariable("FLUX_API_KEY")); using var request = new HttpRequestMessage(HttpMethod.Post, $"{baseUrl}/api/v1/invoices"); request.Headers.Add("Idempotency-Key", "invoice-ws_42-2026-11"); request.Content = new StringContent(""" { "externalRef": "ws_42-2026-11", "customerId": 812, "currency": "NGN", "dueDate": "2026-11-15", "discount": 0, "periodStart": "2026-11-01", "periodEnd": "2026-11-30", "invoiceType": "subscription", "nextDueDate": "2026-12-01", "comments": "November subscription", "lines": [ { "serviceId": 7, "quantity": 3, "unitPrice": 250000, "detail": "3 entities" }, { "serviceId": 9, "amount": 50000 } ] } """, Encoding.UTF8, "application/json"); using var response = await http.SendAsync(request); Console.WriteLine($"{(int)response.StatusCode} {await response.Content.ReadAsStringAsync()}");package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "externalRef": "ws_42-2026-11", "customerId": 812, "currency": "NGN", "dueDate": "2026-11-15", "discount": 0, "periodStart": "2026-11-01", "periodEnd": "2026-11-30", "invoiceType": "subscription", "nextDueDate": "2026-12-01", "comments": "November subscription", "lines": [ { "serviceId": 7, "quantity": 3, "unitPrice": 250000, "detail": "3 entities" }, { "serviceId": 9, "amount": 50000 } ] }`) req, err := http.NewRequest("POST", os.Getenv("FLUX_BASE_URL")+"/api/v1/invoices", body) if err != nil { panic(err) } req.Header.Set("Authorization", "ApiKey "+os.Getenv("FLUX_API_KEY")) req.Header.Set("Content-Type", "application/json") req.Header.Set("Idempotency-Key", "invoice-ws_42-2026-11") resp, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer resp.Body.Close() out, _ := io.ReadAll(resp.Body) fmt.Println(resp.StatusCode, string(out)) }201 Created
{ "id": 5012, "number": "MAX-2026-0042", "externalRef": "ws_42-2026-11", "customerId": 812, "currency": "NGN", "status": "awaiting_approval", "overdue": false, "paymentStatus": null, "invoiceType": "subscription", "periodStart": "2026-11-01T00:00:00", "periodEnd": "2026-11-30T00:00:00", "nextDueDate": "2026-12-01T00:00:00", "createdAt": "2026-11-01T09:00:00", "approvedAt": null, "dueDate": "2026-11-15T00:00:00", "paidAt": null, "subtotal": 800000.00, "discount": 0.00, "taxableNet": 800000.00, "vatAmount": 60000.00, "vatLabel": "VAT (7.5%)", "total": 860000.00, "lines": [ { "serviceId": 7, "description": "Subscription — 3 entities (3 × 250,000.00)", "detail": "3 entities", "quantity": 3, "unitPrice": 250000.00, "amount": 750000.00 }, { "serviceId": 9, "description": "Onboarding", "detail": null, "quantity": 1, "unitPrice": 50000.00, "amount": 50000.00 } ], "payments": null }Nothing goes to the customer yetThe invoice is
awaiting_approval. Flux notifies the organization's reviewers, not the customer. The customer gets the invoice by email only when it's approved. Approve it in the staging app to seeinvoice.approvedandinvoice.issuedarrive.Send the same request again. You get
200and the same invoice, because theexternalRefwas already used. That's how you avoid double billing.Receive your first webhook#
Register an
httpsURL you control. For a quick look, use a request inspector; for real work, your own endpoint.curl -X POST "$FLUX_BASE_URL/api/v1/webhook-endpoints" \ -H "Authorization: ApiKey $FLUX_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: endpoint-quickstart" \ -d '{ "url": "https://hooks.example.com/flux", "description": "Quick start", "events": ["*"] }'// Node.js 18+ (built-in fetch). Save as an .mjs file. const response = await fetch(`${process.env.FLUX_BASE_URL}/api/v1/webhook-endpoints`, { method: "POST", headers: { Authorization: `ApiKey ${process.env.FLUX_API_KEY}`, "Content-Type": "application/json", "Idempotency-Key": "endpoint-quickstart", }, body: JSON.stringify({ url: "https://hooks.example.com/flux", description: "Quick start", events: ["*"], }), }); console.log(response.status, await response.json());import os import requests response = requests.post( f"{os.environ['FLUX_BASE_URL']}/api/v1/webhook-endpoints", headers={ "Authorization": f"ApiKey {os.environ['FLUX_API_KEY']}", "Idempotency-Key": "endpoint-quickstart", }, json={ "url": "https://hooks.example.com/flux", "description": "Quick start", "events": ["*"], }, timeout=30, ) print(response.status_code, response.json())<?php $body = <<<'JSON' { "url": "https://hooks.example.com/flux", "description": "Quick start", "events": ["*"] } JSON; $ch = curl_init(getenv('FLUX_BASE_URL') . '/api/v1/webhook-endpoints'); curl_setopt_array($ch, [ CURLOPT_CUSTOMREQUEST => 'POST', CURLOPT_HTTPHEADER => [ 'Authorization: ApiKey ' . getenv('FLUX_API_KEY'), 'Content-Type: application/json', 'Idempotency-Key: endpoint-quickstart', ], CURLOPT_POSTFIELDS => $body, CURLOPT_RETURNTRANSFER => true, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_HTTP_CODE), ' ', $response, PHP_EOL;using System.Net.Http.Headers; using System.Text; var baseUrl = Environment.GetEnvironmentVariable("FLUX_BASE_URL"); using var http = new HttpClient(); http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("ApiKey", Environment.GetEnvironmentVariable("FLUX_API_KEY")); using var request = new HttpRequestMessage(HttpMethod.Post, $"{baseUrl}/api/v1/webhook-endpoints"); request.Headers.Add("Idempotency-Key", "endpoint-quickstart"); request.Content = new StringContent(""" { "url": "https://hooks.example.com/flux", "description": "Quick start", "events": ["*"] } """, Encoding.UTF8, "application/json"); using var response = await http.SendAsync(request); Console.WriteLine($"{(int)response.StatusCode} {await response.Content.ReadAsStringAsync()}");package main import ( "fmt" "io" "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "url": "https://hooks.example.com/flux", "description": "Quick start", "events": ["*"] }`) req, err := http.NewRequest("POST", os.Getenv("FLUX_BASE_URL")+"/api/v1/webhook-endpoints", body) if err != nil { panic(err) } req.Header.Set("Authorization", "ApiKey "+os.Getenv("FLUX_API_KEY")) req.Header.Set("Content-Type", "application/json") req.Header.Set("Idempotency-Key", "endpoint-quickstart") resp, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer resp.Body.Close() out, _ := io.ReadAll(resp.Body) fmt.Println(resp.StatusCode, string(out)) }The response includes
secret, the signing secret. It's shown once. Store it with your API key. Then send a test event:curl -X POST "$FLUX_BASE_URL/api/v1/webhook-endpoints/3/test" \ -H "Authorization: ApiKey $FLUX_API_KEY"// Node.js 18+ (built-in fetch). Save as an .mjs file. const response = await fetch(`${process.env.FLUX_BASE_URL}/api/v1/webhook-endpoints/3/test`, { method: "POST", headers: { Authorization: `ApiKey ${process.env.FLUX_API_KEY}`, }, }); console.log(response.status, await response.text());import os import requests response = requests.post( f"{os.environ['FLUX_BASE_URL']}/api/v1/webhook-endpoints/3/test", headers={"Authorization": f"ApiKey {os.environ['FLUX_API_KEY']}"}, timeout=30, ) print(response.status_code, response.text)<?php $ch = curl_init(getenv('FLUX_BASE_URL') . '/api/v1/webhook-endpoints/3/test'); curl_setopt_array($ch, [ CURLOPT_CUSTOMREQUEST => 'POST', CURLOPT_HTTPHEADER => [ 'Authorization: ApiKey ' . getenv('FLUX_API_KEY'), ], CURLOPT_RETURNTRANSFER => true, ]); $response = curl_exec($ch); echo curl_getinfo($ch, CURLINFO_HTTP_CODE), ' ', $response, PHP_EOL;using System.Net.Http.Headers; var baseUrl = Environment.GetEnvironmentVariable("FLUX_BASE_URL"); using var http = new HttpClient(); http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("ApiKey", Environment.GetEnvironmentVariable("FLUX_API_KEY")); using var request = new HttpRequestMessage(HttpMethod.Post, $"{baseUrl}/api/v1/webhook-endpoints/3/test"); using var response = await http.SendAsync(request); Console.WriteLine($"{(int)response.StatusCode} {await response.Content.ReadAsStringAsync()}");package main import ( "fmt" "io" "net/http" "os" ) func main() { req, err := http.NewRequest("POST", os.Getenv("FLUX_BASE_URL")+"/api/v1/webhook-endpoints/3/test", nil) if err != nil { panic(err) } req.Header.Set("Authorization", "ApiKey "+os.Getenv("FLUX_API_KEY")) resp, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer resp.Body.Close() out, _ := io.ReadAll(resp.Body) fmt.Println(resp.StatusCode, string(out)) }Within a few seconds your endpoint receives:
POST /flux HTTP/1.1 Host: hooks.example.com Content-Type: application/json User-Agent: FluxERP-Webhooks/1 Flux-Event-Id: evt_01JC8Z5T6Q1W3M4N5P6R7S8T9V Flux-Event-Type: webhook.test Flux-Signature: t=1793528400,v1=7d6b8ca702474b03c774aa73d62e80609c1db53535dbeb2d19bab0eeede8af5f{ "id": "evt_01JC8Z5T6Q1W3M4N5P6R7S8T9V", "type": "webhook.test", "created": "2026-11-01T10:20:00.4121933Z", "data": { "object": { "endpointId": 3, "message": "Test event from Flux ERP" } } }Respond with any 2xx within 10 seconds. Then verify the signature before you trust a delivery.
Next steps#
Bill without double billing
Deterministic
externalRefper customer and period.Build a billing page
Statement totals, invoices, and download and pay buttons.
Receive webhooks reliably
Verify, respond fast, deduplicate, re-read.
Go-live checklist
The eight things a production integration does.