Invoice creation example

Detailed Example

This section walks you through two common scenarios using the Wafeq APIcreating an invoice and creating a cash invoice — with complete request and response examples in JavaScript, Python, and Go.

Each example shows how to:

  • Create or retrieve the necessary data (contacts, accounts)
  • Create the invoice or cash invoice
  • Record a payment (where applicable)

These examples assume you already have your API key and workspace configured.
If not, start with the Authentication guide first.

Create a contact

Create a customer contact you can bill later.

Endpoint: POST https://api.wafeq.com/v1/contacts/
Auth: Authorization: Api-Key <YOUR_API_KEY>
See the Contacts reference for the complete schema and additional fields.

Request body (minimal example)

{
  "name": "Acme Trading LLC",
  "email": "[email protected]",
  "phone": "+971500000000"
}	

Tip: Add any extra fields you track (e.g., address data, tax registration number) per the reference.

Request

const res = await fetch("https://api.wafeq.com/v1/contacts/", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Authorization": "Api-Key " + process.env.WAFEQ_API_KEY
  },
  body: JSON.stringify({
    name: "Acme Trading LLC",
    email: "[email protected]",
    phone: "+971500000000"
  })
});

if (!res.ok) {
  const err = await res.text();
  throw new Error(`Create contact failed: ${res.status} ${err}`);
}

const contact = await res.json();
console.log(contact);
import os, requests

resp = requests.post(
    "https://api.wafeq.com/v1/contacts/",
    headers={
        "Authorization": f"Api-Key {os.environ['WAFEQ_API_KEY']}",
        "Content-Type": "application/json",
    },
    json={
        "name": "Acme Trading LLC",
        "email": "[email protected]",
        "phone": "+971500000000",
    },
)
resp.raise_for_status()
contact = resp.json()
print(contact)
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"os"
)

func main() {
	body := map[string]any{
		"name":  "Acme Trading LLC",
		"email": "[email protected]",
		"phone": "+971500000000",
	}
	b, _ := json.Marshal(body)

	req, _ := http.NewRequest("POST", "https://api.wafeq.com/v1/contacts/", bytes.NewReader(b))
	req.Header.Set("Authorization", "Api-Key "+os.Getenv("WAFEQ_API_KEY"))
	req.Header.Set("Content-Type", "application/json")

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()
	if resp.StatusCode >= 300 {
		msg, _ := io.ReadAll(resp.Body)
		panic(fmt.Errorf("create contact failed: %s %s", resp.Status, string(msg)))
	}

	var contact map[string]any
	if err := json.NewDecoder(resp.Body).Decode(&contact); err != nil {
		panic(err)
	}
	fmt.Println(contact)
}

Retrieve the account to book the sales to

Choose the revenue account you’ll post the invoice line(s) to (e.g., “Sales”).
If you already know the account_id, you can skip this step and use it directly when creating the invoice line item.

Endpoint: GET https://api.wafeq.com/v1/accounts/
Auth: Authorization: Api-Key <YOUR_API_KEY>

Tip: In your own workspace, prefer filtering by a known code (e.g., 4000) or an exact name (e.g., “Sales”) to avoid ambiguity.

const res = await fetch("https://api.wafeq.com/v1/accounts/", {
  headers: {
    "Authorization": "Api-Key " + process.env.WAFEQ_API_KEY
  }
});

if (!res.ok) {
  const err = await res.text();
  throw new Error(`List accounts failed: ${res.status} ${err}`);
}

const { results } = await res.json();

const revenueAccount =
  results.find(a => a.code === "4000") ||
  results.find(a => a.name?.toLowerCase() === "sales") ||
  results.find(a => a.classification === "revenue" || a.type === "revenue");

if (!revenueAccount) {
  throw new Error("No suitable revenue account found. Consider using a known code or name.");
}

console.log(revenueAccount);
import os, requests

resp = requests.get(
    "https://api.wafeq.com/v1/accounts/",
    headers={"Authorization": f"Api-Key {os.environ['WAFEQ_API_KEY']}"},
)
resp.raise_for_status()
payload = resp.json()

results = payload.get("results", payload if isinstance(payload, list) else [])
revenue_account = next(
    (
        a for a in results
        if a.get("code") == "4000"
        or (a.get("name") or "").lower() == "sales"
        or a.get("classification") == "revenue"
        or a.get("type") == "revenue"
    ),
    None
)

if not revenue_account:
    raise RuntimeError("No suitable revenue account found. Use a known code or exact name.")

print(revenue_account)
package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"os"
	"strings"
)

type Account struct {
	ID             string `json:"id"`
	Name           string `json:"name"`
	Code           string `json:"code"`
	Type           string `json:"type"`
	Classification string `json:"classification"`
}

func main() {
	req, _ := http.NewRequest("GET", "https://api.wafeq.com/v1/accounts/", nil)
	req.Header.Set("Authorization", "Api-Key "+os.Getenv("WAFEQ_API_KEY"))

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()
	if resp.StatusCode >= 300 {
		panic(fmt.Errorf("list accounts failed: %s", resp.Status))
	}

	// Support either {"results":[...]} or bare [] shape
	var payload struct {
		Results []Account `json:"results"`
	}
	var list []Account

	dec := json.NewDecoder(resp.Body)
	if err := dec.Decode(&payload); err == nil && len(payload.Results) > 0 {
		list = payload.Results
	} else {
		// Try decode as a bare array (ignore prior decode error)
		req2, _ := http.NewRequest("GET", "https://api.wafeq.com/v1/accounts/", nil)
		req2.Header = req.Header
		resp2, err2 := http.DefaultClient.Do(req2)
		if err2 != nil {
			panic(err2)
		}
		defer resp2.Body.Close()
		if err := json.NewDecoder(resp2.Body).Decode(&list); err != nil {
			panic(err)
		}
	}

	var revenue *Account
	for i := range list {
		a := &list[i]
		if a.Code == "4000" ||
			strings.EqualFold(a.Name, "Sales") ||
			a.Classification == "revenue" ||
			a.Type == "revenue" {
			revenue = a
			break
		}
	}

	if revenue == nil {
		panic("no suitable revenue account found; use a known code or exact name")
	}
	fmt.Printf("%+v\n", *revenue)
}

Example Response

 {
      "account_code": "11",
      "account_type": "",
      "classification": "ASSET",
      "created_ts": "2025-10-01T06:50:48.985071Z",
      "id": "acc_7mWREbbgavfh6JCGsQCSkz",
      "is_locked": true,
      "is_payment_enabled": false,
      "is_posting": false,
      "is_system": false,
      "modified_ts": "2025-10-01T06:50:48.985145Z",
      "name_ar": "الأصول المتداولة",
      "name_en": "Current Assets",
      "parent": "acc_FSD8TuroF3ywNFky7zZ5e9",
      "sub_classification": "CURRENT_ASSET"
 },

Create the invoice

Create an empty invoice “header”, then add line items.
Endpoints:

  • POST https://api.wafeq.com/v1/invoices/ (create invoice) — see Invoices.
  • POST https://api.wafeq.com/v1/invoices/{invoice_id}/line-items/ (add line) — see Invoice Line Items.

Auth: Authorization: Api-Key <YOUR_API_KEY> Idempotency (recommended for POSTs): X-Wafeq-Idempotency-Key: <uuid-v4> — see Idempotency.

You’ll use the contact you created earlier and the revenue account you retrieved in the previous step.

Request body (invoice header — minimal)

{
  "contact": "cnt_7c2f2a1f-1f7e-4c2f-9f8a-1c2a0b9e3d11",
  "currency": "AED",
  "invoice_date": "2025-10-30",
  "invoice_due_date": "2025-11-14",
  "invoice_number": "INV-1001",
  "notes": "Thanks for your business!",
  "line_items": [
    {
      "account": "acc_9a1c7f3b-2d4e-4a53-9a3b-b5f7e2c4e9d0",
      "description": "Consulting services",
      "quantity": 10,
      "unit_amount": 250,
      "tax_rate": null
    }
  ]
}

Request

const API = "https://api.wafeq.com/v1";
const headers = {
  "Content-Type": "application/json",
  "Authorization": "Api-Key " + process.env.WAFEQ_API_KEY,
  "X-Wafeq-Idempotency-Key": crypto.randomUUID()
};

const payload = {
  contact: process.env.WAFEQ_CONTACT_ID,
  currency: "AED",
  invoice_date: "2025-10-30",
  invoice_due_date: "2025-11-14",
  invoice_number: "INV-1001",
  notes: "Thanks for your business!",
  line_items: [
    {
      account: process.env.WAFEQ_REVENUE_ACCOUNT_ID, // e.g., Sales
      description: "Consulting services",
      quantity: 10,
      unit_amount: 250,
      tax_rate: null
    }
  ]
};

const invRes = await fetch(`${API}/invoices/`, {
  method: "POST",
  headers,
  body: JSON.stringify(payload)
});

if (!invRes.ok) {
  const err = await invRes.text();
  throw new Error(`Create invoice failed: ${invRes.status} ${err}`);
}

const invoice = await invRes.json();
console.log(invoice);
import os, uuid, requests

API = "https://api.wafeq.com/v1"
headers = {
    "Authorization": f"Api-Key {os.environ['WAFEQ_API_KEY']}",
    "Content-Type": "application/json",
    "X-Wafeq-Idempotency-Key": str(uuid.uuid4()),
}

payload = {
    "contact": os.environ["WAFEQ_CONTACT_ID"],
    "currency": "AED",
    "invoice_date": "2025-10-30",
    "invoice_due_date": "2025-11-14",
    "invoice_number": "INV-1001",
    "notes": "Thanks for your business!",
    "line_items": [
        {
            "account": os.environ["WAFEQ_REVENUE_ACCOUNT_ID"],
            "description": "Consulting services",
            "quantity": 10,
            "unit_amount": 250,
            "tax_rate": None,
        }
    ],
}

resp = requests.post(f"{API}/invoices/", headers=headers, json=payload)
resp.raise_for_status()
invoice = resp.json()
print(invoice)
import os, uuid, requests

API = "https://api.wafeq.com/v1"
headers = {
    "Authorization": f"Api-Key {os.environ['WAFEQ_API_KEY']}",
    "Content-Type": "application/json",
    "X-Wafeq-Idempotency-Key": str(uuid.uuid4()),
}

payload = {
    "contact": os.environ["WAFEQ_CONTACT_ID"],
    "currency": "AED",
    "invoice_date": "2025-10-30",
    "invoice_due_date": "2025-11-14",
    "invoice_number": "INV-1001",
    "notes": "Thanks for your business!",
    "line_items": [
        {
            "account": os.environ["WAFEQ_REVENUE_ACCOUNT_ID"],
            "description": "Consulting services",
            "quantity": 10,
            "unit_amount": 250,
            "tax_rate": None,
        }
    ],
}

resp = requests.post(f"{API}/invoices/", headers=headers, json=payload)
resp.raise_for_status()
invoice = resp.json()
print(invoice)

Record a payment on the invoice

Create a customer payment and link it to the invoice you just created.

Endpoint: POST https://api.wafeq.com/v1/payments/
Auth: Authorization: Api-Key <YOUR_API_KEY>
Idempotency (recommended): X-Wafeq-Idempotency-Key: <uuid-v4>

You’ll need:

  • The invoice ID (from the previous step)
  • The contact ID
  • The paid-through account ID (your bank or cash account)

Request body (example)

{
  "amount": 2500.0,
  "contact": "cnt_7c2f2a1f-1f7e-4c2f-9f8a-1c2a0b9e3d11",
  "currency": "AED",
  "date": "2025-10-31",
  "paid_through_account": "acc_bank_123",
  "reference": "Bank transfer FT-92831",
  "invoice_payments": [
    {
      "invoice": "inv_2af9a6e1-2c10-4b2a-b111-0d7b2f2f0e9a",
      "amount": 2500.0,
      "amount_to_pcy": 2500.0
    }
  ]
}

Request

const res = await fetch("https://api.wafeq.com/v1/payments/", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Authorization": "Api-Key " + process.env.WAFEQ_API_KEY,
    "X-Wafeq-Idempotency-Key": crypto.randomUUID()
  },
  body: JSON.stringify({
    amount: 2500.0,
    contact: process.env.WAFEQ_CONTACT_ID,
    currency: "AED",
    date: "2025-10-31",
    paid_through_account: process.env.WAFEQ_BANK_ACCOUNT_ID,
    reference: "Bank transfer FT-92831",
    invoice_payments: [
      {
        invoice: process.env.WAFEQ_INVOICE_ID,
        amount: 2500.0,
        amount_to_pcy: 2500.0
      }
    ]
  })
});

if (!res.ok) {
  const err = await res.text();
  throw new Error(`Create payment failed: ${res.status} ${err}`);
}

const payment = await res.json();
console.log(payment);
import os, uuid, requests

resp = requests.post(
    "https://api.wafeq.com/v1/payments/",
    headers={
        "Authorization": f"Api-Key {os.environ['WAFEQ_API_KEY']}",
        "Content-Type": "application/json",
        "X-Wafeq-Idempotency-Key": str(uuid.uuid4()),
    },
    json={
        "amount": 2500.0,
        "contact": os.environ["WAFEQ_CONTACT_ID"],
        "currency": "AED",
        "date": "2025-10-31",
        "paid_through_account": os.environ["WAFEQ_BANK_ACCOUNT_ID"],
        "reference": "Bank transfer FT-92831",
        "invoice_payments": [
            {
                "invoice": os.environ["WAFEQ_INVOICE_ID"],
                "amount": 2500.0,
                "amount_to_pcy": 2500.0,
            }
        ],
    },
)
resp.raise_for_status()
print(resp.json())
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"os"

	"github.com/google/uuid"
)

func main() {
	payload := map[string]any{
		"amount":              2500.0,
		"contact":             os.Getenv("WAFEQ_CONTACT_ID"),
		"currency":            "AED",
		"date":                "2025-10-31",
		"paid_through_account": os.Getenv("WAFEQ_BANK_ACCOUNT_ID"),
		"reference":           "Bank transfer FT-92831",
		"invoice_payments": []map[string]any{
			{
				"invoice":       os.Getenv("WAFEQ_INVOICE_ID"),
				"amount":        2500.0,
				"amount_to_pcy": 2500.0,
			},
		},
	}

	b, _ := json.Marshal(payload)
	req, _ := http.NewRequest("POST", "https://api.wafeq.com/v1/payments/", bytes.NewReader(b))
	req.Header.Set("Authorization", "Api-Key "+os.Getenv("WAFEQ_API_KEY"))
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("X-Wafeq-Idempotency-Key", uuid.NewString())

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()

	if resp.StatusCode >= 300 {
		panic(fmt.Errorf("create payment failed: %s", resp.Status))
	}

	fmt.Println("Payment recorded successfully")
}