Ruby SDK
Send invoices to Complyance from a Ruby application: install the gem, send a sample payload for mapping, then submit invoices and follow them. You need Ruby 2.6 or later and a sandbox API key.
Install
The gem is io-complyance-unify-sdk. The latest version is 3.0.12, a stable release.
gem "io-complyance-unify-sdk", "~> 3.0"Then run bundle install.
gem install io-complyance-unify-sdk -v 3.0.12In your code, load it with require "complyance_sdk".
Configure the SDK
Configure the SDK once, when your application starts.
require "complyance_sdk"
config = ComplyanceSDK::Config::SDKConfig.new(
api_key: ENV.fetch("COMPLYANCE_API_KEY"),
environment: ComplyanceSDK::Models::Environment::SANDBOX
)
ComplyanceSDK.configure(config)
SOURCE_NAME = "acme-erp"
SOURCE_VERSION = "1"environmentisEnvironment::SANDBOXfor testing andEnvironment::PRODUCTIONfor live invoices. Use a sandbox key with sandbox and a production key with production.SOURCE_NAMEandSOURCE_VERSIONname the system that sends the invoices, such as your ERP. Use the same pair for mapping and for invoicing, because Complyance uses it to find the mapping for your invoices.
Send a sample payload for mapping
Before you can submit invoices, Complyance needs to know how your fields match 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 = {
invoice_number: "INV-2026-0142",
issue_date: "2026-09-22",
currency: "AED",
seller: { name: "Acme Trading LLC", trn: "100000000000003" },
buyer: { name: "Gulf Retail LLC", trn: "100000000000011" },
lines: [
{ description: "Office chairs", quantity: 4, unit_price: 250.0, vat_rate: 5 }
],
total: 1050.0
}
document_type = ComplyanceSDK::Models::GetsDocumentType.builder
.base(ComplyanceSDK::Models::BASE::TAX_INVOICE)
.build
raw = ComplyanceSDK.push_to_unify_v2(
SOURCE_NAME,
SOURCE_VERSION,
document_type,
ComplyanceSDK::Models::Country::AE,
ComplyanceSDK::Models::Operation::SINGLE,
ComplyanceSDK::Models::Mode::DOCUMENTS,
ComplyanceSDK::Models::Purpose::MAPPING,
payload
)
result = ComplyanceSDK::Models::UnifyResponse.from_h(raw)
puts result.statusYou should see success. The payload is now stored in your workspace.
Next, open the Integration engine in the Developer portal. In Payload Selection, choose the payload you sent, then map your fields to GETS and link the integration to your source.
Submit an invoice
Once the mapping is linked to your source, send invoices with the purpose set to invoicing. Use the same source name and version as for mapping.
The document type is a GETS document type: a base, such as BASE::TAX_INVOICE or BASE::CREDIT_NOTE, plus any modifiers that apply, such as MODIFIER::EXPORT. It decides which fields the invoice must contain.
document_type = ComplyanceSDK::Models::GetsDocumentType.builder
.base(ComplyanceSDK::Models::BASE::TAX_INVOICE)
.modifiers([ComplyanceSDK::Models::MODIFIER::B2B])
.build
raw = ComplyanceSDK.push_to_unify_v2(
SOURCE_NAME,
SOURCE_VERSION,
document_type,
ComplyanceSDK::Models::Country::AE,
ComplyanceSDK::Models::Operation::SINGLE,
ComplyanceSDK::Models::Mode::DOCUMENTS,
ComplyanceSDK::Models::Purpose::INVOICING,
payload
)
result = ComplyanceSDK::Models::UnifyResponse.from_h(raw)
if result.success?
document_id = result.data.dig("document", "documentId")
puts "Accepted: #{document_id}"
else
result.validation_errors.each { |error| puts error }
end{
"status": "success",
"data": {
"document": {
"documentId": "01K5EXAMPLE00000000000142"
}
}
}success? is true only when the status is success and the response lists no validation errors. Keep the documentId to follow the invoice.
If the API cannot be reached, the SDK keeps the invoice and sends it again later. The status is then queued.
Run your invoices in sandbox, and through a testbed, before you switch environment to production.
Check a document’s status
Delivery to the tax authority continues after the response. Look up the document with its documentId.
status = ComplyanceSDK.get_document_status("01K5EXAMPLE00000000000142")
data = status["data"]
puts data["state"]
puts data["isTerminal"]{
"success": true,
"data": {
"documentId": "01K5EXAMPLE00000000000142",
"documentNumber": "INV-2026-0142",
"country": "AE",
"environment": "sandbox",
"state": "final_valid",
"isTerminal": true,
"lastUpdatedAt": "2026-09-22T10:00:04.000Z"
}
}While isTerminal is false, the invoice is still being processed, so check again later. final_valid means it was accepted as compliant. To be told instead of asking, use webhooks.
Retrieve purchase invoices
Purchase invoices are the invoices your suppliers send you. To read them, the SDK also needs your workspace ID as client_id. If you do not have it, ask support@complyance.io.
config = ComplyanceSDK::Config::SDKConfig.new(
api_key: ENV.fetch("COMPLYANCE_API_KEY"),
environment: ComplyanceSDK::Models::Environment::SANDBOX,
client_id: ENV.fetch("COMPLYANCE_CLIENT_ID")
)
ComplyanceSDK.configure(config)List the purchase invoices received in a date range. Each page holds up to limit invoices. While hasMore is true, ask for the next page with nextCursor, keeping the same dates and limit.
cursor = nil
loop do
page = ComplyanceSDK.list_purchase_invoices(
from: "2026-09-01",
to: "2026-09-30",
limit: 100,
cursor: cursor
)["data"]
page["items"].each do |item|
puts "#{item["invoiceNumber"]} from #{item["supplierName"]}"
end
break unless page["hasMore"]
cursor = page["nextCursor"]
endGet one purchase invoice by its documentId:
invoice = ComplyanceSDK.get_purchase_invoice("01JEXAMPLE0000000000000142")
puts invoice.document_number
puts invoice.state
puts invoice.is_terminalEvery field is described in List purchase invoices and Get a purchase invoice.
Handle errors
Every error the SDK raises is a ComplyanceSDK::Exceptions::SDKException, with a message and, where there is one, a suggestion.
begin
raw = ComplyanceSDK.push_to_unify_v2(
SOURCE_NAME, SOURCE_VERSION, document_type,
ComplyanceSDK::Models::Country::AE,
ComplyanceSDK::Models::Operation::SINGLE,
ComplyanceSDK::Models::Mode::DOCUMENTS,
ComplyanceSDK::Models::Purpose::INVOICING,
payload
)
rescue ComplyanceSDK::Exceptions::APIError => e
puts "#{e.status_code}: #{e.message}"
puts e.context[:response_body]
rescue ComplyanceSDK::Exceptions::SDKException => e
puts e.message
puts e.suggestion if e.suggestion
end| Error | What it means | What to do |
|---|---|---|
APIError with status_code 401 | The API key is missing, invalid, revoked or expired. | Check COMPLYANCE_API_KEY, or create a new API key. |
APIError with status_code 403 | The key belongs to a different environment from the one you configured. | Use a sandbox key with Environment::SANDBOX and a production key with Environment::PRODUCTION. |
ValidationError: Country not allowed for environment | The SDK refused the country before sending anything. | Use a country the SDK accepts for this environment, such as Country::AE. |
ConfigurationError: SDK must be configured before making API calls | ComplyanceSDK.configure was not called, or the key or environment is missing. | Configure the SDK before the first call. |
NetworkError means the SDK could not reach Complyance. What each status code from the API means is in Errors.
Last updated