Skip to main content
POST
/
transactions
Create Transaction
curl --request POST \
  --url http://api.gu1.ai/transactions \
  --header 'Authorization: Bearer <token>' \
  --header 'Content-Type: application/json' \
  --data '
{
  "externalId": "<string>",
  "type": "<string>",
  "amount": 123,
  "currency": "<string>",
  "exchangeRate": 123,
  "status": "<string>",
  "paymentMethod": "<string>",
  "description": "<string>",
  "category": "<string>",
  "transactedAt": "<string>",
  "executeRules": true,
  "asyncRules": true,
  "configRulesExecution": {},
  "validateExistingEntity": true,
  "riskMatrixId": [
    "<string>"
  ],
  "riskMatrixIds": [
    "<string>"
  ],
  "originEntityId": "<string>",
  "originExternalId": "<string>",
  "originTaxId": "<string>",
  "originName": "<string>",
  "originCountry": "<string>",
  "originDetails": {},
  "destinationEntityId": "<string>",
  "destinationExternalId": "<string>",
  "destinationTaxId": "<string>",
  "destinationName": "<string>",
  "destinationCountry": "<string>",
  "destinationDetails": {},
  "locationDetails": {},
  "deviceDetails": {},
  "channel": "<string>",
  "reason": "<string>",
  "timeZone": "<string>",
  "metadata": {}
}
'
import requests

url = "http://api.gu1.ai/transactions"

payload = {
"externalId": "<string>",
"type": "<string>",
"amount": 123,
"currency": "<string>",
"exchangeRate": 123,
"status": "<string>",
"paymentMethod": "<string>",
"description": "<string>",
"category": "<string>",
"transactedAt": "<string>",
"executeRules": True,
"asyncRules": True,
"configRulesExecution": {},
"validateExistingEntity": True,
"riskMatrixId": ["<string>"],
"riskMatrixIds": ["<string>"],
"originEntityId": "<string>",
"originExternalId": "<string>",
"originTaxId": "<string>",
"originName": "<string>",
"originCountry": "<string>",
"originDetails": {},
"destinationEntityId": "<string>",
"destinationExternalId": "<string>",
"destinationTaxId": "<string>",
"destinationName": "<string>",
"destinationCountry": "<string>",
"destinationDetails": {},
"locationDetails": {},
"deviceDetails": {},
"channel": "<string>",
"reason": "<string>",
"timeZone": "<string>",
"metadata": {}
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}

response = requests.post(url, json=payload, headers=headers)

print(response.text)
const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
externalId: '<string>',
type: '<string>',
amount: 123,
currency: '<string>',
exchangeRate: 123,
status: '<string>',
paymentMethod: '<string>',
description: '<string>',
category: '<string>',
transactedAt: '<string>',
executeRules: true,
asyncRules: true,
configRulesExecution: {},
validateExistingEntity: true,
riskMatrixId: ['<string>'],
riskMatrixIds: ['<string>'],
originEntityId: '<string>',
originExternalId: '<string>',
originTaxId: '<string>',
originName: '<string>',
originCountry: '<string>',
originDetails: {},
destinationEntityId: '<string>',
destinationExternalId: '<string>',
destinationTaxId: '<string>',
destinationName: '<string>',
destinationCountry: '<string>',
destinationDetails: {},
locationDetails: {},
deviceDetails: {},
channel: '<string>',
reason: '<string>',
timeZone: '<string>',
metadata: {}
})
};

fetch('http://api.gu1.ai/transactions', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));
<?php

$curl = curl_init();

curl_setopt_array($curl, [
CURLOPT_URL => "http://api.gu1.ai/transactions",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'externalId' => '<string>',
'type' => '<string>',
'amount' => 123,
'currency' => '<string>',
'exchangeRate' => 123,
'status' => '<string>',
'paymentMethod' => '<string>',
'description' => '<string>',
'category' => '<string>',
'transactedAt' => '<string>',
'executeRules' => true,
'asyncRules' => true,
'configRulesExecution' => [

],
'validateExistingEntity' => true,
'riskMatrixId' => [
'<string>'
],
'riskMatrixIds' => [
'<string>'
],
'originEntityId' => '<string>',
'originExternalId' => '<string>',
'originTaxId' => '<string>',
'originName' => '<string>',
'originCountry' => '<string>',
'originDetails' => [

],
'destinationEntityId' => '<string>',
'destinationExternalId' => '<string>',
'destinationTaxId' => '<string>',
'destinationName' => '<string>',
'destinationCountry' => '<string>',
'destinationDetails' => [

],
'locationDetails' => [

],
'deviceDetails' => [

],
'channel' => '<string>',
'reason' => '<string>',
'timeZone' => '<string>',
'metadata' => [

]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);

$response = curl_exec($curl);
$err = curl_error($curl);

curl_close($curl);

if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}
package main

import (
"fmt"
"strings"
"net/http"
"io"
)

func main() {

url := "http://api.gu1.ai/transactions"

payload := strings.NewReader("{\n \"externalId\": \"<string>\",\n \"type\": \"<string>\",\n \"amount\": 123,\n \"currency\": \"<string>\",\n \"exchangeRate\": 123,\n \"status\": \"<string>\",\n \"paymentMethod\": \"<string>\",\n \"description\": \"<string>\",\n \"category\": \"<string>\",\n \"transactedAt\": \"<string>\",\n \"executeRules\": true,\n \"asyncRules\": true,\n \"configRulesExecution\": {},\n \"validateExistingEntity\": true,\n \"riskMatrixId\": [\n \"<string>\"\n ],\n \"riskMatrixIds\": [\n \"<string>\"\n ],\n \"originEntityId\": \"<string>\",\n \"originExternalId\": \"<string>\",\n \"originTaxId\": \"<string>\",\n \"originName\": \"<string>\",\n \"originCountry\": \"<string>\",\n \"originDetails\": {},\n \"destinationEntityId\": \"<string>\",\n \"destinationExternalId\": \"<string>\",\n \"destinationTaxId\": \"<string>\",\n \"destinationName\": \"<string>\",\n \"destinationCountry\": \"<string>\",\n \"destinationDetails\": {},\n \"locationDetails\": {},\n \"deviceDetails\": {},\n \"channel\": \"<string>\",\n \"reason\": \"<string>\",\n \"timeZone\": \"<string>\",\n \"metadata\": {}\n}")

req, _ := http.NewRequest("POST", url, payload)

req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")

res, _ := http.DefaultClient.Do(req)

defer res.Body.Close()
body, _ := io.ReadAll(res.Body)

fmt.Println(string(body))

}
HttpResponse<String> response = Unirest.post("http://api.gu1.ai/transactions")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"externalId\": \"<string>\",\n \"type\": \"<string>\",\n \"amount\": 123,\n \"currency\": \"<string>\",\n \"exchangeRate\": 123,\n \"status\": \"<string>\",\n \"paymentMethod\": \"<string>\",\n \"description\": \"<string>\",\n \"category\": \"<string>\",\n \"transactedAt\": \"<string>\",\n \"executeRules\": true,\n \"asyncRules\": true,\n \"configRulesExecution\": {},\n \"validateExistingEntity\": true,\n \"riskMatrixId\": [\n \"<string>\"\n ],\n \"riskMatrixIds\": [\n \"<string>\"\n ],\n \"originEntityId\": \"<string>\",\n \"originExternalId\": \"<string>\",\n \"originTaxId\": \"<string>\",\n \"originName\": \"<string>\",\n \"originCountry\": \"<string>\",\n \"originDetails\": {},\n \"destinationEntityId\": \"<string>\",\n \"destinationExternalId\": \"<string>\",\n \"destinationTaxId\": \"<string>\",\n \"destinationName\": \"<string>\",\n \"destinationCountry\": \"<string>\",\n \"destinationDetails\": {},\n \"locationDetails\": {},\n \"deviceDetails\": {},\n \"channel\": \"<string>\",\n \"reason\": \"<string>\",\n \"timeZone\": \"<string>\",\n \"metadata\": {}\n}")
.asString();
require 'uri'
require 'net/http'

url = URI("http://api.gu1.ai/transactions")

http = Net::HTTP.new(url.host, url.port)

request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"externalId\": \"<string>\",\n \"type\": \"<string>\",\n \"amount\": 123,\n \"currency\": \"<string>\",\n \"exchangeRate\": 123,\n \"status\": \"<string>\",\n \"paymentMethod\": \"<string>\",\n \"description\": \"<string>\",\n \"category\": \"<string>\",\n \"transactedAt\": \"<string>\",\n \"executeRules\": true,\n \"asyncRules\": true,\n \"configRulesExecution\": {},\n \"validateExistingEntity\": true,\n \"riskMatrixId\": [\n \"<string>\"\n ],\n \"riskMatrixIds\": [\n \"<string>\"\n ],\n \"originEntityId\": \"<string>\",\n \"originExternalId\": \"<string>\",\n \"originTaxId\": \"<string>\",\n \"originName\": \"<string>\",\n \"originCountry\": \"<string>\",\n \"originDetails\": {},\n \"destinationEntityId\": \"<string>\",\n \"destinationExternalId\": \"<string>\",\n \"destinationTaxId\": \"<string>\",\n \"destinationName\": \"<string>\",\n \"destinationCountry\": \"<string>\",\n \"destinationDetails\": {},\n \"locationDetails\": {},\n \"deviceDetails\": {},\n \"channel\": \"<string>\",\n \"reason\": \"<string>\",\n \"timeZone\": \"<string>\",\n \"metadata\": {}\n}"

response = http.request(request)
puts response.read_body
{
  "transaction": {},
  "rulesExecutionSummary": {},
  "asyncRules": true,
  "rulesEvaluationStatus": "<string>"
}

Overview

Creates a new transaction. With executeRules: true (default), the rules engine runs synchronously and the response includes a populated rulesExecutionSummary at the root when rules finish in the same request. Omitting asyncRules (default false) preserves this behavior β€” existing integrations are unchanged.

Endpoint

POST http://api.gu1.ai/transactions

Authentication

Requires a valid API key in the Authorization header:
Authorization: Bearer YOUR_API_KEY

Query Parameters

asyncRules
boolean
default:"false"
When true and executeRules is not false, the transaction is created immediately and rules evaluation is enqueued for background processing. The HTTP response returns before rules finish. Query param takes precedence over the same field in the JSON body.Accepted truthy values: true, 1, "true", "1", "yes".Not supported on batch create endpoints β€” only POST /transactions (single create).

Request Body

Required Fields

externalId
string
required
Your unique identifier for this transaction in your system
type
string
required
Type of transaction. Options:
  • PAYMENT - Payment transaction
  • TRANSFER - Money transfer
  • WITHDRAWAL - Cash withdrawal
  • DEPOSIT - Cash or check deposit
  • REFUND - Refund transaction
  • CHARGEBACK - Chargeback
  • REVERSAL - Transaction reversal
  • FEE - Service fee
  • ADJUSTMENT - Balance adjustment
  • OTHER - Other transaction type
amount
number
required
Transaction amount (must be zero or positive)
currency
string
required
Currency code (3-4 characters, e.g., β€œUSD”, β€œEUR”, β€œBRL”)
exchangeRate
number
Optional custom exchange rate, used only when automatic conversion to your organization’s base currency is unavailable (provider error, timeout, or unsupported pair). Omit this field to keep the existing behavior β€” Gueno calls the currency service as today.Semantics: base-currency units per 1 unit of currency. Normalized amount in your organization’s base currency: normalizedAmount = amount Γ— exchangeRate.Ignored when automatic conversion succeeds (provider rate wins).Required when automatic conversion is unavailable for non-convertible currencies (see below). See Currency conversion.

Optional Fields

status
string
default:"CREATED"
Transaction status. Options:
  • CREATED - Transaction created (default)
  • PROCESSING - Being processed
  • SUSPENDED - Suspended for review
  • SENT - Successfully sent
  • EXPIRED - Transaction expired
  • DECLINED - Declined/rejected
  • REFUNDED - Refunded
  • SUCCESSFUL - Completed successfully
paymentMethod
string
Payment method used. Options:
  • CARD - Credit/debit card
  • ACH - ACH transfer
  • PIX - Brazilian PIX
  • TED - Brazilian TED
  • BOLETO - Brazilian Boleto
  • WALLET - Digital wallet
  • SWIFT - SWIFT transfer
  • IBAN - IBAN transfer
  • CBU - Argentine CBU
  • CVU - Argentine CVU
  • DEBIN - Argentine DEBIN
  • GENERIC_BANK_ACCOUNT - Generic bank account
  • MPESA - M-Pesa
  • UPI - UPI (India)
  • CHECK - Check payment
  • ECHECK - Electronic check
  • QR_CODE - QR code payment
  • ONLINE_PAYMENT - Online payment
  • WITHDRAWAL_ORDER - Withdrawal order
description
string
Transaction description or notes
category
string
Transaction category for classification
transactedAt
string
ISO 8601 datetime when the transaction occurred (defaults to creation time). Stored as UTC. Use Z or Β±HH:MM in the string, or send a naive datetime together with timeZone (local wall time in that zone).
executeRules
boolean
default:"true"
Whether to run the rules engine for this transaction. Set to false to skip rules entirely (sync and async).
asyncRules
boolean
default:"false"
Same semantics as the query param asyncRules. Use for high-volume ingestion when you need the transaction persisted quickly and can review alerts later in gu1. Requires executeRules: true (default). Ignored when executeRules is false.
configRulesExecution
object
Optional tuning for how rules run after create (sync or async). Does not disable createAlert actions or investigation consolidation.
FieldTypeDefault
notificationsbooleantrue for most organizations; false for Paytime prod (3bc1f621-27d4-423e-9d64-86680bec2388) when omitted
When notifications is false, gu1 skips in-app notifications from rules evaluation (risk matrix / status change toasts). Alerts and investigations still behave normally.Legacy KYT POST /legacy/kyt/verifyTransaction accepts the same object on the Gu2 body as configRulesExecution; if omitted, Paytime prod gets notifications: false by default (same as POST /transactions).
validateExistingEntity
boolean
default:"false"
With false (default): if you send originExternalId / destinationExternalId (or root-level tax ids) and no entity matches, the transaction is still created (unlinked), same as before.With true: each side (origin / destination) is validated only when you send at least one identifier for that side (originEntityId, originExternalId, or originTaxId; same for destination). Every identifier you send must resolve to an existing person/company in your organization; otherwise the API returns 400 with INVALID_ENTITY_REFERENCES and does not create the transaction. If you send no origin (or destination) identifiers, that side is not validated.

Risk Matrices (Optional)

riskMatrixId
string | string[]
Legacy-compatible: one UUID or an array of UUIDs of risk matrices owned by your organization. When non-empty, only rules assigned to those matrices run for this transaction (no mixing with β€œloose” trigger-only rules). Omit both riskMatrixId and riskMatrixIds to keep the historical trigger-based behavior.
riskMatrixIds
string[]
Preferred for multiple matrices: ordered UUID list. Takes precedence over riskMatrixId when provided and non-empty.

Origin Entity Fields

How the origin is linked in gu1 (tried in order; stop at first success):
  1. Direct ID β€” if you send originEntityId, the transaction is tied to that entity (must exist in your org).
  2. External ID β€” if you did not send originEntityId but you send originExternalId and a person/company exists with the same externalId, the row is auto-linked to that entity.
  3. Tax / document ID (third fallback) β€” if the transaction is still not linked, but you send root originTaxId, the API looks up a person/company whose taxId in gu1 matches after normalization (only letters and digits, ignoring punctuation, spaces, and case for comparison).
If step 2 or 3 succeeds, the API sets originEntityId and, when you did not provide them, enriches originName and originCountry from the matched entity.Canonical denormalization (linked origin): whenever the origin side is linked to a person/company β€” you sent originEntityId, or auto-link succeeded via originExternalId / originTaxId β€” gu1 syncs denormalized columns from the entity row before insert:
Transaction fieldSource on entityClient value kept?
originEntityIdentities.idSet on auto-link; unchanged if you already sent a valid UUID
originTaxIdentities.tax_idNo β€” always overwritten from the linked entity
originExternalIdentities.external_idNo β€” always overwritten from the linked entity
If the entity has no taxId or externalId, the corresponding transaction column is stored as null, even when you sent values in the request body.Integrator note: you can send any mix of identifiers to establish the link (precedence: originEntityId β†’ originExternalId β†’ originTaxId). After linking, persisted originTaxId / originExternalId reflect the entity in gu1, not necessarily what you typed. This keeps transaction monitoring rules aligned with user events (entityId, entityExternalId, taxId on the event row).If the tax/external value does not match any entity, the transaction is still created; the fields you sent are stored, but there is no entity link.
(For graph visualization only, you can also send a tax in originDetails β€” that path does not auto-link; use originTaxId at the root when you want a real link by document.)
originEntityId
string
UUID of the origin entity (sender) in gu1 system
originExternalId
string
Your external ID for the origin entity. Used as the second linking key when originEntityId is omitted. After a successful link, the stored value is entities.external_id (client value is not kept if it differs).
originTaxId
string
Tax or national document ID of the origin counterparty (e.g. CPF, CNPJ, CUIT), at the root of the transaction. Used only as the third way to find and link a person/company, after originEntityId and originExternalId. The value is compared to each entity’s taxId using a normalized form (alphanumeric only). If a match is found, originEntityId is set and name/country can be filled. After a successful link, the stored value is entities.tax_id. Max length: 50 characters. Optional.
originName
string
Name of the origin entity
originCountry
string
ISO 2-letter country code of origin (e.g., β€œUS”, β€œBR”, β€œAR”)
originDetails
object
Detailed information about the origin (sender/device). These fields match the API schema; you can also send additional custom fields and they will be stored.Schema fields (all optional):Device/Technical:
  • deviceId (string) - Unique device identifier
  • deviceFingerprint (string) - Device fingerprint hash
  • deviceType (enum) - β€˜mobile’, β€˜desktop’, β€˜tablet’, β€˜pos’, β€˜atm’
  • userAgent (string) - Browser user agent
  • ipAddress (string) - IP address (validated format)
Geolocation:
  • country (string) - ISO 2-letter country code
  • city (string), region (string), latitude (number), longitude (number), timezone (string)
Account:
  • accountNumber (string), accountType (enum: β€˜checking’, β€˜savings’, β€˜business’, β€˜personal’), bankCode (string), bankName (string)
Security flags:
  • isVpn (boolean), isTor (boolean), isProxy (boolean), governmentAccount (boolean)
paymentDetails (optional): Nested object with payment-level data (card, PIX, bank, etc.). Not enforced β€” you can send any keys. See Payment Details Schema for suggested structures by payment method (card, PIX, CBU/CVU, SPEI, PSE, DEBIN, crypto, wallet, cash, check, etc.). Extra and custom fields are allowed.Unknown origin (no entity): When the origin party is not an entity in gu1 (you don’t send originEntityId or originExternalId), you can still improve network graph visualization by sending identifying data in originDetails.paymentDetails. The graph groups pseudo nodes by the first match in this order: taxId, cbu, cvu, pixKey, clabe, alias, iban, holderId, debinId, cardFingerprint, then accountNumber (+ optional bankCode). See Payment Details Schema β€” Unknown party. Optional and internal only.

Destination Entity Fields

Destination linking follows the same precedence as origin: destinationEntityId β†’ destinationExternalId β†’ destinationTaxId.When the destination is linked, gu1 always syncs destinationTaxId and destinationExternalId from the matched entity (same rules as origin). Unlinked sides keep the values you sent.
destinationEntityId
string
UUID of the destination entity (recipient) in gu1 system
destinationExternalId
string
Your external ID for the destination entity. After a successful link, stored as entities.external_id.
destinationTaxId
string
Tax or document ID of the destination counterparty at the root of the request. Third linking fallback: used only if destinationEntityId and destinationExternalId did not resolve. Same matching rules as originTaxId. When a match is found, destinationEntityId is set and name/country can be enriched. After a successful link, stored as entities.tax_id. Max: 50 characters. Optional.
destinationName
string
Name of the destination entity
destinationCountry
string
ISO 2-letter country code of destination (e.g., β€œUS”, β€œBR”, β€œAR”)
destinationDetails
object
Detailed information about the destination (merchant/receiver). These fields match the API schema; you can also send additional custom fields and they will be stored.Schema fields (all optional):Merchant:
  • mcc (string) - Merchant Category Code (4 digits)
  • mccDescription (string), merchantId (string), merchantName (string), merchantType (string)
Device/Technical:
  • deviceId (string), deviceType (enum: β€˜pos’, β€˜online’, β€˜mobile’, β€˜atm’), ipAddress (string)
Geolocation:
  • country (string, ISO 2), city (string), region (string)
Account:
  • accountNumber (string), accountType (enum: β€˜checking’, β€˜savings’, β€˜business’, β€˜merchant’), bankCode (string), bankName (string)
Risk flags:
  • cryptoExchange (boolean), highRisk (boolean), privateSector (boolean)
paymentDetails (optional): Nested object with payment-level data (merchant account, card, PIX, etc.). Not enforced β€” you can send any keys. See Payment Details Schema for suggested structures by payment method. Extra and custom fields are allowed.Unknown destination (no entity): When the destination party is not an entity in gu1 (you don’t send destinationEntityId or destinationExternalId), you can still improve network graph visualization by sending identifying data in destinationDetails.paymentDetails (same priority order as origin: taxId, cbu, cvu, pixKey, clabe, alias, iban, holderId, debinId, cardFingerprint, then accountNumber + optional bankCode). See Payment Details Schema β€” Unknown party. Optional and internal only.

Location Details

locationDetails
object
Physical location information for the transaction. Useful for fraud detection and geographic analysis.Address Information:
  • country (string) - ISO 2-letter country code
  • countryName (string) - Full country name
  • city (string) - City name
  • region (string) - State/Province
  • address (string) - Full address
  • street (string) - Street name
  • streetNumber (string) - Street number
  • postalCode (string) - Postal/ZIP code
  • neighborhood (string) - Neighborhood/district
Coordinates (for map visualization):
  • latitude (number) - GPS latitude (-90 to 90)
  • longitude (number) - GPS longitude (-180 to 180)
Additional Info:
  • timezone (string) - Timezone identifier
  • placeId (string) - Google Places ID or similar

Device Details

deviceDetails
object
Detailed device information for fraud detection and device fingerprinting.Device Identification:
  • deviceId (string) - Unique device identifier
  • externalId (string) - Your external device ID
Platform and OS:
  • platform (enum) - Platform: β€˜android’, β€˜ios’, β€˜web’, β€˜desktop’, β€˜mobile’, β€˜tablet’, β€˜pos’, β€˜atm’
  • osName (string) - OS name (e.g., β€˜Android’, β€˜iOS’, β€˜Windows’, β€˜macOS’)
  • osVersion (string) - OS version
Device Info:
  • manufacturer (string) - Device manufacturer
  • model (string) - Device model
  • brand (string) - Device brand
  • deviceName (string) - Device name/nickname
Browser Info (for web platform):
  • browser (string) - Browser name
  • browserVersion (string) - Browser version
  • userAgent (string) - Full user agent string
Security Flags:
  • isEmulator (boolean) - Device is an emulator
  • isRooted (boolean) - Android device is rooted
  • isJailbroken (boolean) - iOS device is jailbroken
Network Info:
  • ipAddress (string) - IP address (validated format)
  • isVpn (boolean) - Connection via VPN
  • isTor (boolean) - Connection via Tor network
  • isProxy (boolean) - Connection via proxy
Device Fingerprint:
  • deviceFingerprint (string) - Unique device fingerprint hash
Additional Details:
  • screenResolution (string) - Screen resolution (e.g., β€œ1920x1080”)
  • language (string) - Device language
  • timezone (string) - Device timezone

Channel

channel
string
Channel through which the transaction originated (max 50 characters).Common values:
  • mobile_app - Mobile application
  • web_browser - Web browser
  • pos_terminal - Point of sale terminal
  • api - Direct API integration
  • atm - ATM machine
  • phone_banking - Phone banking
  • branch - Physical branch
  • chatbot - Chatbot interface
  • third_party - Third-party integration

Reason

reason
string
Optional reason for the transaction outcome (e.g. decline, failure, limit exceeded). Send any value from the transaction_reason_type enum. If omitted, the system uses WITHOUT_REASON. Not required β€” existing integrations remain valid.Full list: See Transaction Reason Enum for all 60+ allowed values.

Time Zone

timeZone
string
Optional IANA time zone for transaction-local context (independent of entity operationalHours). Send a value from transaction_time_zone (same enum values as operationalHours.timezone). Omit for null.transactedAt normalization: If transactedAt includes Z (as required by this endpoint’s datetime validator), that instant is stored in UTC; timeZone is optional metadata and is not used for parsing. If you also send timeZone together with a local datetime (internal/batch tooling), the API can interpret naive wall time in that zone. Existing integrations that omit timeZone behave as before. KYT operational-hours rules use the stored UTC instant plus the entity’s operationalHours.timezone, not transaction.timeZone.Full list: See Transaction Time Zone Enum.

Metadata

metadata
object
Additional metadata for the transaction. Optional fields:Tags:
  • tags (object) - Key-value pairs for categorization (values can be string, number, or boolean)
Transaction Context:
  • purpose (string) - Purpose of the transaction
  • frequency (string) - Transaction frequency
  • contract_number (string) - Associated contract number
Compliance:
  • enhanced_due_diligence (boolean) - EDD flag
  • block_reason (string) - Reason for blocking
  • compliance_alert (boolean) - Compliance alert flag
Custom fields are allowed via passthrough behavior.

Response

transaction
object
The created transaction object. Includes:
  • id - gu1’s internal transaction ID
  • externalId - Your external ID
  • organizationId - Your organization ID
  • type - Transaction type
  • amount - Transaction amount (string)
  • currency - Currency code
  • status - Transaction status
  • riskScore - Calculated risk score 0-100 (string)
  • flagged - Whether transaction is flagged
  • channel - Channel information
  • reason - Outcome reason (e.g. WITHOUT_REASON, INSUFFICIENT_FUNDS)
  • timeZone - IANA time zone (string | null)
  • originDetails / destinationDetails - Origin/destination details
  • locationDetails - Location data
  • deviceDetails - Device information
  • processingTimeMs, processedAt, transactedAt, createdAt, updatedAt - Timestamps
rulesExecutionSummary
object
Present when executeRules is true.Synchronous (default): populated after rules finish in the same request (rulesHit, rulesNoHit, scores, etc.).Async (asyncRules=true): placeholder with success: true, empty rulesHit / rulesNoHit, and matchedRulesCount: 0. Alerts and risk updates appear after background processing completes.Omitted when executeRules is false.See Rules Execution Summary for the full structure and a complete example.
asyncRules
boolean
Present and true only when rules were queued (asyncRules=true). Omitted in the default synchronous flow.
rulesEvaluationStatus
string
When async: "queued". Omitted when rules ran synchronously.

Examples

Basic Transaction

curl -X POST http://api.gu1.ai/transactions \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "externalId": "txn_12345",
    "type": "PAYMENT",
    "amount": 150.50,
    "currency": "USD",
    "originExternalId": "customer_001",
    "destinationExternalId": "merchant_456",
    "description": "Online purchase"
  }'
const response = await fetch('http://api.gu1.ai/transactions', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer YOUR_API_KEY',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    externalId: 'txn_12345',
    type: 'PAYMENT',
    amount: 150.50,
    currency: 'USD',
    originExternalId: 'customer_001',
    destinationExternalId: 'merchant_456',
    description: 'Online purchase'
  })
});

const data = await response.json();
console.log(data.transaction);
import requests

response = requests.post(
    'http://api.gu1.ai/transactions',
    headers={
        'Authorization': 'Bearer YOUR_API_KEY',
        'Content-Type': 'application/json'
    },
    json={
        'externalId': 'txn_12345',
        'type': 'PAYMENT',
        'amount': 150.50,
        'currency': 'USD',
        'originExternalId': 'customer_001',
        'destinationExternalId': 'merchant_456',
        'description': 'Online purchase'
    }
)

transaction = response.json()['transaction']
print(transaction)

Async Rules (High-Volume Ingestion)

Use when you need fast 201 responses and will review alerts in gu1 later. Rules run in the background; rulesExecutionSummary.rulesHit is empty in the HTTP response.
curl -X POST "http://api.gu1.ai/transactions?asyncRules=true" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "externalId": "txn_nightly_001",
    "type": "PAYMENT",
    "amount": 150.50,
    "currency": "BRL",
    "destinationExternalId": "merchant_456"
  }'
{
  "transaction": { "id": "...", "externalId": "txn_nightly_001", "status": "SUCCESSFUL" },
  "rulesExecutionSummary": {
    "success": true,
    "rulesHit": [],
    "rulesNoHit": [],
    "totalScore": 0,
    "matchedRulesCount": 0,
    "executionTimeMs": 0
  },
  "asyncRules": true,
  "rulesEvaluationStatus": "queued"
}
Do not use the synchronous response to block or approve payments when asyncRules=true. There is no second webhook when background rules finish β€” monitor alerts in gu1 or poll transaction/investigation APIs.

Currency Conversion

When currency differs from your organization’s base currency (default USD), Gueno fetches an exchange rate automatically. Behavior is unchanged if you omit exchangeRate.

Resolution Order

StepConditionResult
1currency equals base currencyrateSource: no-conversion, exchangeRate: 1
2Automatic conversion succeedsProvider rate (ms-provider, cache, etc.)
3Automatic conversion fails and you sent exchangeRaterateSource: client-provided
4Automatic conversion fails, no exchangeRaterateSource: conversion-unavailable; no normalized base-currency amount

exchangeRate Semantics

  • Direction: base-currency units per 1 unit of currency (same as provider rates).
  • Formula: normalizedAmount = amount Γ— exchangeRate (stored on the transaction in your org base currency).
  • Not an override: if step 2 succeeds, a client exchangeRate is ignored.

Non-Convertible Currencies (Automatic Conversion)

Gueno does not obtain exchange rates automatically for these codes today. Automatic conversion returns unavailable unless you send exchangeRate:
CurrencyCodeNotes
WorldcoinWLDClient must supply exchangeRate for normalized base amount
EthereumETHClient must supply exchangeRate for normalized base amount
All other ISO codes follow the normal automatic conversion flow (provider or historical rate when transactedAt is set). Send exchangeRate when you need a normalized amount in base currency for rules and reporting.

Example β€” WLD with Client Rate

{
  "externalId": "txn-wld-001",
  "type": "PAYMENT",
  "amount": 26.16,
  "currency": "WLD",
  "exchangeRate": 2.41,
  "destinationExternalId": "merchant_001"
}
Response (org base currency USD β€” normalized amount 63.05, rateSource: client-provided):
{
  "transaction": {
    "amount": "26.16",
    "currency": "WLD",
    "exchangeRate": "2.41",
    "rateSource": "client-provided",
    "currenciesExchange": [
      { "currency": "USD", "exchangeRate": 2.41, "value": 63.05 }
    ]
  }
}
Batch create (POST /transactions/batch, upload, JSON) accepts the same optional exchangeRate on each row with identical semantics.
When Redis is configured, jobs go to the shared transaction-rules-eval queue (dedicated workers if DISABLE_INPROCESS_BULL_WORKERS=true). If Redis is missing or enqueue fails, the API still returns 200 with asyncRules: true and runs rules in-process on that server (best for single-instance or dev; high-volume multi-instance deployments should use Redis + workers).

Response Example

{
  "transaction": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "externalId": "txn_67890",
    "organizationId": "8e2f89ab-c216-4eb4-90eb-ca5d44499aaa",
    "type": "TRANSFER",
    "status": "CREATED",
    "amount": "1000.00",
    "currency": "BRL",
    "amountInUsd": "1000.00",
    "paymentMethod": "PIX",
    "riskScore": "15",
    "flagged": false,
    "channel": "mobile_app",
    "reason": "WITHOUT_REASON",
    "originDetails": {},
    "destinationDetails": {},
    "locationDetails": {},
    "deviceDetails": {},
    "processingTimeMs": "120",
    "processedAt": "2024-10-03T14:30:00.000Z",
    "transactedAt": "2024-10-03T14:30:00.000Z",
    "createdAt": "2024-10-03T14:30:00.000Z",
    "updatedAt": "2024-10-03T14:30:00.000Z"
  },
  "rulesExecutionSummary": {
    "rulesHit": [],
    "rulesNoHit": [],
    "actionsExecuted": {},
    "totalScore": 15
  }
}
(For batch create, see Create Batch; that endpoint returns an array of transactions.)

Error Responses

400 Bad Request - Invalid Data

{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Validation failed",
    "details": [
      "Amount must be zero or a positive number",
      "Currency must be at least 3 characters"
    ]
  }
}

400 Bad Request - Invalid IP Address

{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Invalid deviceDetails structure",
    "details": {
      "path": ["deviceDetails", "ipAddress"],
      "message": "Invalid IP address format"
    }
  }
}

409 Conflict - Duplicate Transaction

{
  "success": false,
  "error": {
    "code": "DUPLICATE_TRANSACTION",
    "message": "Transaction with this external_id already exists",
    "details": {
      "field": "external_id",
      "value": "txn_12345"
    }
  }
}

429 Too Many Requests

{
  "error": "Rate limit exceeded",
  "code": "RATE_LIMIT_EXCEEDED",
  "retryAfter": 3600
}

Next Steps