Go SDK
Send invoices to Complyance from a Go application, from a first sample payload to a submitted invoice in sandbox. You need Go 1.18 or later and an API key for your workspace.
Install
go get github.com/complyance-io/complyance-go-sdk/v3@v3.0.3The code lives in the module’s pkg package. The examples on this page import it as sdk:
import sdk "github.com/complyance-io/complyance-go-sdk/v3/pkg"Configure the SDK
Configure the SDK once, when your application starts. Every call after that uses the same key and environment.
package main
import (
"log"
"os"
sdk "github.com/complyance-io/complyance-go-sdk/v3/pkg"
)
const (
sourceName = "acme-erp"
sourceVersion = "1.0.0"
)
func main() {
cfg := sdk.NewSDKConfig(
os.Getenv("COMPLYANCE_API_KEY"),
sdk.EnvironmentSandbox,
nil, // sources
nil, // retry settings: nil uses the defaults
)
if err := sdk.Configure(cfg); err != nil {
log.Fatal(err)
}
}- Read the key from the
COMPLYANCE_API_KEYenvironment variable. Never write it in your code. sdk.EnvironmentSandboxsends to sandbox. Usesdk.EnvironmentProductionwith a production key when you go live. The key and the environment must match.sourceNameandsourceVersionname the source your invoices come from, as set up in your workspace. Every request sends them.
This version of the SDK keeps its configuration for the whole process, so its functions take no client value and no context.Context. Each call waits for the answer, for up to 30 seconds per attempt.
Send a sample payload for mapping
Before you can submit invoices, Complyance needs to know how your fields correspond to GETS, the Complyance standard invoice format. Send one invoice exactly as your system produces it, with the purpose set to mapping. Nothing is sent to a tax authority.
payload := map[string]interface{}{
"invoice_no": "INV-2026-0142",
"invoice_date": "2026-09-18",
"currency": "AED",
"seller_name": "Acme Trading LLC",
"customer_name": "Gulf Office Supplies LLC",
"lines": []map[string]interface{}{
{"description": "Office chairs", "qty": 4, "unit_price": 250.00, "vat_rate": 5},
},
"total": 1050.00,
}
resp, err := sdk.PushToUnifyV2(
sourceName,
sourceVersion,
sdk.DocType.Of(sdk.BASE.TaxInvoice),
sdk.CountryAE,
sdk.OperationSingle,
sdk.ModeDocuments,
sdk.PurposeMapping,
payload,
nil, // destinations
)
if err != nil {
log.Fatal(err)
}
log.Println("status:", resp.GetStatus())A nil error means Complyance accepted the payload.
Next, open the Integration engine in the Developer portal. In Create an integration, pick the payload you sent at Payload Selection, then map your fields to GETS and test your mapping.
Submit an invoice
Once the integration is tested and linked to your source, send invoices from the same source with the purpose set to invoicing. Send the payload in the same format you used for mapping; Complyance converts it with your integration.
docType := sdk.DocType.Of(sdk.BASE.TaxInvoice)
resp, err := sdk.PushToUnifyV2(
sourceName,
sourceVersion,
docType,
sdk.CountryAE,
sdk.OperationSingle,
sdk.ModeDocuments,
sdk.PurposeInvoicing,
payload,
nil, // destinations
)
if err != nil {
// See "Handle errors" below.
log.Fatal(err)
}
log.Println("status:", resp.GetStatus())The document type is a GETS base plus optional modifiers. The base is one of sdk.BASE.TaxInvoice, sdk.BASE.SimplifiedInvoice, sdk.BASE.CreditNote or sdk.BASE.DebitNote. Add modifiers after it for the kind of sale:
docType := sdk.DocType.Of(sdk.BASE.TaxInvoice, sdk.MODIFIER.Export)Which bases and modifiers a country accepts, and which fields each one requires, is in the field reference.
In sandbox and production, version v3.0.3 sends documents for Saudi Arabia (sdk.CountrySA), Malaysia (sdk.CountryMY) and the UAE (sdk.CountryAE). Any other country returns an INVALID_ARGUMENT error before the request is sent.
Run your invoices in sandbox first, and check them against the test cases in a testbed before you switch to production.
Check a document’s status
Version v3.0.3 has no function for a document’s status. Follow each document with webhooks, or with the request in Follow the document.
Retrieve purchase invoices
Purchase invoices are the invoices your suppliers send you. Get one by its documentId:
invoice, err := sdk.GetPurchaseInvoice("01JEXAMPLE0000000000000142")
if err != nil {
log.Fatal(err)
}
data, _ := invoice["data"].(map[string]interface{})
log.Println(data["documentNumber"], data["state"])To list the invoices received in a date range, pass the query parameters as a map. When hasMore is true, send the same request again with cursor set to nextCursor:
filters := map[string]string{
"from": "2026-09-01",
"to": "2026-09-30",
"limit": "100",
}
for {
page, err := sdk.ListPurchaseInvoices(filters)
if err != nil {
log.Fatal(err)
}
data, _ := page["data"].(map[string]interface{})
items, _ := data["items"].([]interface{})
for _, item := range items {
inv, _ := item.(map[string]interface{})
log.Println(inv["documentId"], inv["invoiceNumber"], inv["supplierName"])
}
hasMore, _ := data["hasMore"].(bool)
next, _ := data["nextCursor"].(string)
if !hasMore || next == "" {
break
}
filters["cursor"] = next
}Both functions return the response body as a map[string]interface{}. Every field is described in Get a purchase invoice and List purchase invoices.
Handle errors
Every function returns an error. When the SDK or the API refuses a request, the error is an *sdk.SDKError. Its ErrorDetail holds a Code, a Message, a Suggestion and, for validation failures, a list of ValidationErrors.
resp, err := sdk.PushToUnifyV2(
sourceName, sourceVersion,
sdk.DocType.Of(sdk.BASE.TaxInvoice),
sdk.CountryAE,
sdk.OperationSingle, sdk.ModeDocuments, sdk.PurposeInvoicing,
payload, nil,
)
var sdkErr *sdk.SDKError
if errors.As(err, &sdkErr) && sdkErr.ErrorDetail != nil && sdkErr.ErrorDetail.Code != nil {
detail := sdkErr.ErrorDetail
switch *detail.Code {
case sdk.ErrorCodeAuthenticationFailed:
log.Fatal("check COMPLYANCE_API_KEY and the environment")
case sdk.ErrorCodeValidationFailed:
for _, v := range detail.ValidationErrors {
log.Printf("%s: %s (%s)", v["field"], v["message"], v["code"])
}
return
default:
log.Fatal(err)
}
} else if err != nil {
log.Fatal(err)
}
if resp.GetStatus() == "queued" {
log.Println("the API was not reachable; the SDK will retry")
}| Code | What it means | What to do |
|---|---|---|
AUTHENTICATION_FAILED | The API key is missing, invalid, revoked or expired. | Check that COMPLYANCE_API_KEY is set, and that the key belongs to the environment you configured. |
VALIDATION_FAILED | The invoice failed validation. Each entry in ValidationErrors names the field, the message and the rule code. | Fix every listed field, then send the invoice again. |
INVALID_ARGUMENT | The request is malformed, or the country is not available in this environment. | Check the country, document type and payload. |
When the API is busy or cannot be reached, the SDK retries the request. If it still fails, the SDK keeps the request and tries again later, and returns no error with the status queued.
What each error from the API means is in Errors.
Last updated