Sign In (T103)
Authenticate the taxpayer session and retrieve device configuration, taxpayer details, branch information, and system settings. This endpoint does not require encryption for the request but returns an encrypted response.
Endpoint Overview
| Property | Value |
|---|---|
| Interface Code | T103 |
| Request Encrypted | ❌ No |
| Response Encrypted | ✅ Yes |
| Request Body | null |
| Response Format | JSON (encrypted payload) |
Flow Description
- Client sends sign-in request after successful
T102: Client Initialization. - Server validates device status, TIN, and cryptographic credentials.
- Server returns:
- Taxpayer profile (
id,tin,legalName,contactdetails) - Device configuration (
deviceNo,validPeriod,offline limits) - Branch information (
branchId,branchName,contactdetails) - Tax type registrations (
VAT,Income Tax, etc.) - System configuration flags (
isAllowBackDate,goodsStockLimit, etc.)
- Taxpayer profile (
- Client stores
taxpayer.idinKeyClientfor subsequent signed requests.
🔐 Security Note: The response is encrypted using the symmetric key obtained from
T104. Ensure yourKeyClientis properly initialized before calling this endpoint.
- PHP
- JavaScript / TypeScript
- Python
try {
// Call T103: Sign In
$response = $client->signIn();
$content = $response['data']['content'] ?? $response;
// Extract taxpayer info and store for later use
if (isset($content['taxpayer']['id'])) {
$keyClient->setTaxpayerId((string) $content['taxpayer']['id']);
echo "✅ Signed in as: {$content['taxpayer']['legalName']}\n";
echo " TIN: {$content['taxpayer']['tin']}\n";
echo " Device: {$content['device']['deviceNo']}\n";
echo " Valid Until: {$content['device']['validPeriod']}\n";
}
// Check configuration flags for business logic
$config = $content;
if ($config['isAllowBackDate'] === '0') {
echo "⚠️ Back-dated invoices are NOT allowed\n";
}
if ($config['goodsStockLimit'] === '101') {
echo "📦 Stock cannot go negative (restricted mode)\n";
}
} catch (\UraEfrisSdk\Exceptions\APIException $e) {
echo "❌ Sign-in failed: " . $e->getMessage() . "\n";
echo " Return Code: " . $e->getReturnCode() . "\n";
}
try {
// Call T103: Sign In
const response = await client.signIn();
const content = response?.data?.content ?? response;
// Extract taxpayer info and store for later use
if (content?.taxpayer?.id) {
keyClient.setTaxpayerId(String(content.taxpayer.id));
console.log('✅ Signed in successfully');
console.log(` Taxpayer: ${content.taxpayer.legalName}`);
console.log(` TIN: ${content.taxpayer.tin}`);
console.log(` Device: ${content.device.deviceNo}`);
console.log(` Valid Until: ${content.device.validPeriod}`);
}
// Check configuration flags for business logic
if (content?.isAllowBackDate === '0') {
console.warn('⚠️ Back-dated invoices are NOT allowed');
}
if (content?.goodsStockLimit === '101') {
console.log('📦 Stock cannot go negative (restricted mode)');
}
return content;
} catch (error: any) {
console.error(`❌ Sign-in failed: ${error.message}`);
if (error.returnCode) {
console.error(` Return Code: ${error.returnCode}`);
}
throw error;
}
try:
# Call T103: Sign In
response = client.sign_in()
content = response.get("data", {}).get("content", response)
# Extract taxpayer info and store for later use
if content.get("taxpayer", {}).get("id"):
key_client.set_taxpayer_id(str(content["taxpayer"]["id"]))
print("✅ Signed in successfully")
print(f" Taxpayer: {content['taxpayer']['legalName']}")
print(f" TIN: {content['taxpayer']['tin']}")
print(f" Device: {content['device']['deviceNo']}")
print(f" Valid Until: {content['device']['validPeriod']}")
# Check configuration flags for business logic
if content.get("isAllowBackDate") == "0":
print("⚠️ Back-dated invoices are NOT allowed")
if content.get("goodsStockLimit") == "101":
print("📦 Stock cannot go negative (restricted mode)")
return content
except Exception as e:
print(f"❌ Sign-in failed: {e}")
if hasattr(e, "return_code"):
print(f" Return Code: {e.return_code}")
raise
Response Structure
{
"data": {
"content": {
"device": {
"deviceModel": "AE320",
"deviceNo": "TCS9e0df01728335239",
"deviceStatus": "252",
"deviceType": "1",
"validPeriod": "19/02/2025",
"offlineAmount": "10000000000",
"offlineDays": "90",
"offlineValue": "1000000000000"
},
"taxpayer": {
"id": "112312313213213213",
"tin": "1000029771",
"ninBrn": "8002598872",
"legalName": "UGANDA REVENUE AUTHORITY",
"businessName": "URA",
"taxpayerStatusId": "101",
"taxpayerType": "202",
"contactEmail": "services@ura.go.ug",
"contactMobile": "0772140000",
"placeOfBusiness": "Nakawa"
},
"taxpayerBranch": {
"branchCode": "02",
"branchName": "Head Office",
"branchType": "101",
"contactName": "Admin",
"contactEmail": "services@ura.go.ug",
"placeOfBusiness": "Nakawa"
},
"taxType": [
{
"taxTypeName": "Value Added Tax",
"taxTypeCode": "301",
"registrationDate": "19/02/2025",
"cancellationDate": "19/02/2030"
}
],
"dictionaryVersion": "1",
"goodsStockLimit": "101",
"isAllowBackDate": "0",
"isAllowIssueInvoice": "1",
"maxGrossAmount": "10000000",
"periodDate": "7",
"creditMemoPeriodDate": "15",
"frequentContactsLimit": "40",
"webServiceURL": "https://api.ura.go.ug/efris/1.0/",
"environment": "0"
}
},
"globalInfo": {
"interfaceCode": "T103",
"returnStateInfo": {
"returnCode": "00",
"returnMessage": "SUCCESS"
}
}
}
Response Fields
Device Configuration
| Field | Required | Type | Description |
|---|---|---|---|
deviceModel | ✅ Yes | String | Equipment model identifier |
deviceNo | ✅ Yes | String | Registered device serial number |
deviceStatus | ✅ Yes | String (3) | Device status code (see dictionary) |
deviceType | ✅ Yes | String (3) | Equipment type code |
validPeriod | ✅ Yes | Date | Device expiry date (dd/MM/yyyy) |
offlineAmount | ✅ Yes | Number | Max invoices allowed in offline mode |
offlineDays | ✅ Yes | Number | Max days device can operate offline |
offlineValue | ✅ Yes | Number | Max total value for offline invoices (UGX) |
Taxpayer Profile
| Field | Required | Type | Description |
|---|---|---|---|
id | ✅ Yes | String (18) | Critical: Taxpayer internal ID (store in KeyClient) |
tin | ✅ Yes | String (50) | Tax Identification Number |
ninBrn | ✅ Yes | String (100) | National ID or Business Registration Number |
legalName | ✅ Yes | String (500) | Registered legal name |
businessName | ✅ Yes | String (500) | Trading/business name |
taxpayerStatusId | ✅ Yes | String (18) | Taxpayer status code (dictionary) |
taxpayerType | ✅ Yes | String (18) | 201=Individual, 202=Non-Individual |
contactEmail | ✅ Yes | String (50) | Primary contact email |
contactMobile | ✅ Yes | String (50) | Primary contact mobile |
placeOfBusiness | ✅ Yes | String (500) | Registered business location |
Taxpayer Branch
| Field | Required | Type | Description |
|---|---|---|---|
branchCode | ❌ No | String (50) | Branch identifier code |
branchName | ❌ No | String (500) | Branch name |
branchType | ❌ No | String (100) | Branch type code (dictionary) |
contactName | ❌ No | String (100) | Branch contact person |
contactEmail | ❌ No | String (50) | Branch contact email |
contactMobile | ❌ No | String (60) | Branch contact mobile |
placeOfBusiness | ❌ No | String (1000) | Branch location |
Tax Type Registrations (Array)
| Field | Required | Type | Description |
|---|---|---|---|
taxTypeName | ✅ Yes | String (200) | Name of registered tax type |
taxTypeCode | ✅ Yes | String (50) | Tax type code (dictionary) |
registrationDate | ✅ Yes | Date | Date tax registration became effective |
cancellationDate | ✅ Yes | Date | Date tax registration expires/cancels |
System Configuration Flags
| Field | Required | Type | Values | Business Impact |
|---|---|---|---|---|
goodsStockLimit | ✅ Yes | String (3) | 101=Restricted, 102=Unlimited | 101 = Stock cannot go negative |
isAllowBackDate | ✅ Yes | String (1) | 0=No, 1=Yes | 0 = Cannot issue invoices prior to current date |
isAllowIssueInvoice | ✅ Yes | String (1) | 0=No, 1=Yes | 0 = Invoice issuance blocked (contact URA) |
isDutyFreeTaxpayer | ✅ Yes | String (1) | 0=No, 1=Yes | 1 = Duty-free shop (special invoice rules) |
isAllowIssueCreditWithoutFDN | ✅ Yes | String (1) | 0=No, 1=Yes | 1 = Can issue credit notes without original FDN |
isAllowOutOfScopeVAT | ✅ Yes | String (1) | 0=No, 1=Yes | 1 = Can issue "VAT Not Applicable" invoices |
periodDate | ✅ Yes | Number | 1-30 | Days allowed to submit credit notes after invoice date |
creditMemoPeriodDate | ✅ Yes | Number | 1-30 | Days allowed for credit memo submissions |
maxGrossAmount | ✅ Yes | Number | UGX value | Maximum allowed invoice gross amount |
frequentContactsLimit | ✅ Yes | Number | 1-100 | Max frequent contacts that can be stored |
buyerModifiedTimes | ✅ Yes | Number | 1-10 | Max times buyer details can be modified per invoice |
buyerModificationPeriod | ✅ Yes | Number | Hours | Time window for modifying buyer details |
Version & Dictionary Fields
| Field | Required | Type | Description |
|---|---|---|---|
dictionaryVersion | ✅ Yes | Number | System dictionary version (compare with local for T115 sync) |
taxpayerBranchVersion | ✅ Yes | String (20) | Branch info version |
commodityCategoryVersion | ✅ Yes | String (10) | Commodity category version (for T134 incremental sync) |
exciseDutyVersion | ✅ Yes | String (10) | Excise duty codes version |
hsCodeVersion | ✅ Yes | Number | HS Code list version |
commGoodsLatestModifyVersion | ✅ Yes | String (14) | Latest goods modification timestamp |
URL & Environment
| Field | Required | Type | Description |
|---|---|---|---|
webServiceURL | ✅ Yes | String (200) | Base API endpoint URL |
qrCodeURL | ✅ Yes | String (200) | Invoice verification page prefix |
environment | ✅ Yes | String (1) | 0=Production, 1=Test/Sandbox |
agentFlag | ✅ Yes | String (1) | 0=Not agent, 1=Agent USSD, 2=Agency Invoicing |
Return Codes
| Code | Message | Description |
|---|---|---|
00 | SUCCESS | Authentication successful; session established |
99 | Unknown error | Generic server error |
100 | Taxpayer does not exist | TIN not found in URA system |
101 | Taxpayer status is abnormal | TIN suspended, deregistered, or inactive |
102 | Taxpayer branch status abnormal | Branch deactivated or suspended |
400 | Device does not exist | deviceNo not registered for this TIN |
401 | Device key does not exist | Cryptographic key missing for device |
402 | Device key expired | Device credentials have expired |
403 | Device status is abnormal | Device blocked, suspended, or requires re-registration |
3088 | Device has been blocked | Contact your system administrator or URA |
💡 Tip: Store the
taxpayer.idimmediately after successful sign-in. All subsequent encrypted requests (T104+) require this ID for signature generation.
Common Use Cases
-
Session Initialization
CallT103afterT102to establish an authenticated session before uploading invoices or querying data. -
Configuration-Driven Logic
Use flags likeisAllowBackDateandgoodsStockLimitto enforce business rules client-side (e.g., block back-dated invoice UI). -
Multi-Branch Management
ExtracttaxpayerBranchdetails to support branch-specific invoicing or stock queries in multi-location deployments. -
Offline Mode Planning
ReadofflineAmount,offlineDays, andofflineValueto implement graceful degradation when network connectivity is lost. -
Compliance Validation
VerifytaxTyperegistrations to ensure you only issue invoice types the taxpayer is authorized for (e.g., VAT vs. non-VAT). -
Dictionary Synchronization
ComparedictionaryVersion,commodityCategoryVersion, etc., with local cache to determine if T115/T134 sync is needed.