API Reference
Complete API documentation for SoftSDC integration
The SoftSDC API enables external applications to communicate with the fiscal service for receipt processing and system monitoring. All communication uses REST principles with JSON payloads over HTTP.
Base URL
http://localhost:8431/api/v3The default port is 8431, but this can be configured in application settings if needed.
Authentication
The API uses PIN-based authentication. Before processing invoices, you must verify the PIN:
POST /api/v3/pinCore Endpoints
Verify PIN
Authenticate with the smart card before processing invoices.
POST /api/v3/pin
Accept: application/json
Content-Type: application/json
{
"pin": "1234"
}Or as plain text:
POST /api/v3/pin
Content-Type: text/plain
12340100Success code indicating PIN is correct.
0100- PIN correct, command executed successfully2100- Incorrect PIN2110- Card locked (exceeded attempts)1300- Smart card not inserted2220- Cannot connect to Secure Element
Create Invoice
Submit receipts for fiscalization.
POST /api/v3/invoices
{
"dateAndTimeOfIssue": "2024-01-15T10:30:00.000Z",
"cashier": "123456789",
"buyerId": "RS34564565",
"buyerCostCenterId": "567546",
"invoiceType": "Normal",
"transactionType": "Sale",
"payment": [
{
"amount": 68.46,
"paymentType": "Cash"
}
],
"invoiceNumber": "POS2024/001",
"referentDocumentNumber": "",
"options": {
"omitQRCodeGen": "0",
"omitTextualRepresentation": "0"
},
"items": [
{
"name": "Product Name",
"quantity": 2,
"unitPrice": 34.23,
"labels": ["A"],
"totalAmount": 68.46
}
]
}{
"verificationUrl": "https://verify.taxauthority.com/...",
"verificationQRCode": "data:image/png;base64,...",
"journal": "============ FISCAL INVOICE ============\n...",
"messages": "Success",
"signedBy": "GFB38TO0",
"encryptedInternalData": "...",
"signature": "...",
"totalCounter": 16,
"transactionTypeCounter": 6,
"totalAmount": 68.46,
"taxGroupRevision": 2,
"businessName": "Company Name",
"tin": "TAXID123",
"locationName": "Store Name",
"address": "Street Address",
"district": "City",
"mrc": "99-0100-GFB38TO0"
}Required Headers:
Accept: application/json
Content-Type: application/jsonOptional Headers:
RequestId: <unique-id> # For tracking (max 32 chars)
Accept-Language: en-US;sr-Cyrl-RS # Preferred languagesGet Status
Check system operational status.
GET /api/v3/status
Accept: application/json{
"isPinRequired": true,
"auditRequired": false,
"sdcDateTime": "2024-01-15T10:30:00.000Z",
"lastInvoiceNumber": "CL4KBJCE-CL4KBJCE-1",
"protocolVersion": "1.0.0.0",
"hardwareVersion": "1.0.0.0",
"softwareVersion": "1.0.0.0",
"mssc": ["6001"],
"gsc": ["0100"],
"deviceSerialNumber": "99-3010-22222222",
"make": "SoftSDC",
"model": "v3.0",
"supportedLanguages": ["en-US", "sr-Cyrl-RS"],
"uid": "TK7SV2AY",
"taxCoreApi": "https://api.taxcore.com/",
"currentTaxRates": {
"validFrom": "2024-01-01T00:00:00Z",
"groupId": 2,
"taxCategories": [
{
"name": "VAT",
"categoryType": 0,
"taxRates": [
{
"rate": 9,
"label": "A"
},
{
"rate": 0,
"label": "B"
}
],
"orderId": 1
}
]
}
}Get Environment Parameters
Retrieve environment configuration.
GET /api/v3/environment-parametersResponse:
{
"organizationName": "Tax Authority",
"serverTimeZone": "Europe/Belgrade",
"street": "Main Street 1",
"city": "Belgrade",
"country": "RS",
"endpoints": {
"taxpayerAdminPortal": "https://portal.taxcore.com/",
"taxCoreApi": "https://api.taxcore.com/",
"vsdc": "https://vsdc.taxcore.com/",
"root": "https://verify.taxcore.com/"
},
"environmentName": "PRODUCTION",
"ntpServer": "http://time.nist.gov/",
"supportedLanguages": ["en-US", "sr-Latn-RS"]
}Health Check
Simple availability check.
GET /api/v3/attentionReturns 200 OK if service is running.
Invoice Types
Transaction Types
Sale- Normal sale transactionRefund- Return/refund transaction
Invoice Types
Normal- Standard fiscal invoiceCopy- Duplicate of existing invoiceTraining- Training mode (non-fiscal)Proforma- Proforma invoiceAdvance- Advance payment invoice
Payment Types
Cash- Cash paymentCard- Credit/debit cardCheck- Check paymentWireTransfer- Bank transferVoucher- Voucher/couponMobileMoney- Mobile payment
Tax Labels
Tax labels identify the tax category for each item. Common labels:
A- Standard VAT rateB- Reduced VAT rateC- VAT exemptD- Special tax categoryE- Service taxN- No tax
Always use tax labels that match your configured tax rates. Invalid labels will result in error 2310.
Error Handling
Status Codes
All responses use standard HTTP status codes:
200- Success400- Bad Request (invalid data)401- Unauthorized (PIN required)500- Internal Server Error
Error Response Format
{
"code": "2100",
"message": "PIN verification failed",
"details": "Invalid PIN provided"
}Common Error Codes
| Code | Type | Description |
|---|---|---|
| 0000 | Info | All OK |
| 0100 | Info | PIN verified successfully |
| 1300 | Warning | Smart card not present |
| 1400 | Warning | Audit required |
| 1500 | Warning | PIN required |
| 2100 | Error | Incorrect PIN |
| 2110 | Error | Card locked |
| 2210 | Error | SE locked |
| 2310 | Error | Invalid tax labels |
| 2400 | Error | Not configured |
| 2800 | Error | Required field missing |
Best Practices
Request Handling
- Always verify PIN first before sending invoices
- Include RequestId for tracking and debugging
- Handle timeouts gracefully (recommend 30s timeout)
- Retry logic for network failures (with exponential backoff)
Performance Tips
- Keep connections alive with HTTP keep-alive
- Batch operations when possible
- Cache status responses for 30-60 seconds
- Monitor response times for degradation
- Implement circuit breakers for resilience
Integration Examples
Basic Integration Flow
sequenceDiagram
POS->>API: POST /pin (authenticate)
API-->>POS: 0100 (success)
POS->>API: GET /status (check ready)
API-->>POS: System status
POS->>API: POST /invoices (create invoice)
API-->>POS: Signed invoice with URL
POS->>Customer: Print receipt with QRSample Implementations
class SoftSDCClient {
constructor(baseUrl = 'http://localhost:8431/api/v3') {
this.baseUrl = baseUrl;
}
async verifyPin(pin) {
const response = await fetch(`${this.baseUrl}/pin`, {
method: 'POST',
headers: { 'Content-Type': 'text/plain' },
body: pin
});
return await response.text();
}
async createInvoice(invoice) {
const response = await fetch(`${this.baseUrl}/invoices`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: JSON.stringify(invoice)
});
return await response.json();
}
}import requests
import json
class SoftSDCClient:
def __init__(self, base_url='http://localhost:8431/api/v3'):
self.base_url = base_url
def verify_pin(self, pin):
response = requests.post(
f'{self.base_url}/pin',
data=pin,
headers={'Content-Type': 'text/plain'}
)
return response.text
def create_invoice(self, invoice):
response = requests.post(
f'{self.base_url}/invoices',
json=invoice,
headers={'Accept': 'application/json'}
)
return response.json()public class SoftSDCClient
{
private readonly HttpClient _client;
private readonly string _baseUrl;
public SoftSDCClient(string baseUrl = "http://localhost:8431/api/v3")
{
_baseUrl = baseUrl;
_client = new HttpClient();
}
public async Task<string> VerifyPin(string pin)
{
var content = new StringContent(pin, Encoding.UTF8, "text/plain");
var response = await _client.PostAsync($"{_baseUrl}/pin", content);
return await response.Content.ReadAsStringAsync();
}
public async Task<InvoiceResponse> CreateInvoice(Invoice invoice)
{
var json = JsonSerializer.Serialize(invoice);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await _client.PostAsync($"{_baseUrl}/invoices", content);
var responseJson = await response.Content.ReadAsStringAsync();
return JsonSerializer.Deserialize<InvoiceResponse>(responseJson);
}
}public class SoftSDCClient {
private final String baseUrl;
private final HttpClient client;
public SoftSDCClient(String baseUrl) {
this.baseUrl = baseUrl;
this.client = HttpClient.newHttpClient();
}
public String verifyPin(String pin) throws Exception {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(baseUrl + "/pin"))
.header("Content-Type", "text/plain")
.POST(HttpRequest.BodyPublishers.ofString(pin))
.build();
HttpResponse<String> response = client.send(
request, HttpResponse.BodyHandlers.ofString()
);
return response.body();
}
public String createInvoice(String invoiceJson) throws Exception {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(baseUrl + "/invoices"))
.header("Content-Type", "application/json")
.header("Accept", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(invoiceJson))
.build();
HttpResponse<String> response = client.send(
request, HttpResponse.BodyHandlers.ofString()
);
return response.body();
}
}Testing
Test Mode
Use invoice type Training for testing without fiscal impact:
{
"invoiceType": "Training",
// ... rest of invoice data
}Test Endpoints
During development, you can use these endpoints for testing:
GET /api/v3/attention- Check if service is runningPOST /api/v3/test-invoice- Generate test invoice (if enabled)
Training mode invoices are marked clearly and do not count toward fiscal reporting.