Welcome to the Directo API - a comprehensive RESTful API for accessing and managing your ERP data including items, orders, customers, invoices, stock levels, and more.
Base URL Structure:
{{baseUrl}}/v{{version}}/{resource}
Current Configuration:
Example Full URL:
[https://login.directo.ee/apidirect/v1/items](https://login.directo.ee/apidirect/v1/items)
The API uses versioning to ensure backward compatibility. Always specify the version number in your requests.
The Directo API uses API Key authentication for secure access.
Include your API key in the request header:
X-Directo-Key: YOUR_API_KEY_HERE
Contact your Directo system administrator to obtain an API key. Each key is associated with specific permissions and access levels.
The API supports both JSON and XML response formats. Control the format using the Accept header.
Accept: application/json
Example Request:
curl -X GET "{{baseUrl}}/v{{version}}/items" -H "X-Directo-Key: YOUR_API_KEY" -H "Accept: application/json"
Accept: application/xml
Example Request:
curl -X GET "{{baseUrl}}/v{{version}}/items" -H "X-Directo-Key: YOUR_API_KEY" -H "Accept: application/xml"
β CRITICAL FEATURE: The Directo API supports the exact same powerful filtering capabilities as the Directo ERP system itself.
This is one of the most powerful features of the Directo API. Filters can be applied like default filters inside the ERP system. All fields can be used for filters except calculated fields.
β Filterable Fields (Stored in Database) These are fields that are stored directly in the database and can be used for filtering:
β Calculated Fields (Cannot Be Filtered) These are fields computed on-the-fly and cannot be used for filtering:
Example 1: Item Stock
β WORKS: GET /items?quantity=>100 (quantity is stored in database) β DOESN'T WORK: GET /items?available_stock=>100 (if available_stock = quantity - reserved, it's calculated)
Example 2: Order Totals
β WORKS: GET /orders?row_price=>1000 (row_price is stored per line item) β MIGHT NOT WORK: GET /orders?order_total=>5000 (if order_total is sum of all line items, it's calculated) β ALTERNATIVE: GET /orders?date=>2025-01-01&status=COMPLETED (then calculate totals in your application)
Example 3: Customer Data
β WORKS: GET /customers?country=EE&city=Tallinn (both are stored fields) β DOESN'T WORK: GET /customers?full_address=*Tallinn* (if full_address is concatenation of address+city+country) β ALTERNATIVE: GET /customers?city=Tallinn (filter by individual stored fields)
Filter by exact match using any stored field name:
GET {{baseUrl}}/v{{version}}/items?code=9810
GET {{baseUrl}}/v{{version}}/items?status=UUS
GET {{baseUrl}}/v{{version}}/orders?number=12345
GET {{baseUrl}}/v{{version}}/customers?country=EE
GET {{baseUrl}}/v{{version}}/items?class=KAUP
Key Principle: Use the exact field name as it appears in the API response (or as you see it in the ERP system).
The API supports powerful comparison operators for advanced filtering:
Greater Than (>)
GET {{baseUrl}}/v{{version}}/orders?date=>2025-09-01T00:00:00
GET {{baseUrl}}/v{{version}}/invoices?row_quantity=>5000
Less Than (<)
GET {{baseUrl}}/v{{version}}/orders?date=<2025-09-03T00:00:00
GET {{baseUrl}}/v{{version}}/invoices?row_quantity=<5000
Not Equal (!)
GET {{baseUrl}}/v{{version}}/items?country=!EE
GET {{baseUrl}}/v{{version}}/orders?status=!CLOSED
Greater Than or Equal (>
GET {{baseUrl}}/v{{version}}/items?price=>=100
GET {{baseUrl}}/v{{version}}/orders?date=>=2025-01-01T00:00:00
Less Than or Equal (β)
GET {{baseUrl}}/v{{version}}/items?weight=<=5.5
GET {{baseUrl}}/v{{version}}/orders?date=<=2025-12-31T23:59:59
Combine operators to create ranges (AND logic):
GET {{baseUrl}}/v{{version}}/orders?date=>2025-09-01T00:00:00&date=<2025-09-03T00:00:00
GET {{baseUrl}}/v{{version}}/items?quantity=>10&quantity=<100
Use comma-separated lists to filter by multiple values:
GET {{baseUrl}}/v{{version}}/items?warehouse=AALTR,AIPM,WAREHOUSE1
GET {{baseUrl}}/v{{version}}/orders?status=NEW,PENDING,PROCESSING
GET {{baseUrl}}/v{{version}}/customers?country=EE,LV,LT
Use the ts parameter to retrieve records modified after a specific timestamp (ideal for synchronization):
GET {{baseUrl}}/v{{version}}/items?ts=>2025-10-01T08:11:05.129Z
GET {{baseUrl}}/v{{version}}/customers?ts=>2025-01-01T00:00:00Z
Best Practice: Store the timestamp of your last successful sync and use it in the next request to fetch only changed records.
For resources with line items (orders, invoices), you can filter by line-level fields:
GET {{baseUrl}}/v{{version}}/orders?row_item=343443
GET {{baseUrl}}/v{{version}}/invoices?row_quantity=>10
Note: When filtering by line item fields, the API returns the entire document (order/invoice) if ANY line matches the filter.
If you have custom fields defined in your ERP system (visible in the datafields array), you can filter by them:
GET {{baseUrl}}/v{{version}}/items?IS_WEIGHT=1
GET {{baseUrl}}/v{{version}}/items?VISUAL_VERIFICATION=1
How to Find Custom Field Names: Make a request without filters and examine the datafields array in the response.
Combine multiple filters to create sophisticated queries:
Get new orders from the last week for a specific customer:
GET {{baseUrl}}/v{{version}}/orders?status=NEW&date=>2025-11-13T00:00:00&customer_code=CUST001
Get items with low stock in specific warehouses, excluding closed items:
GET {{baseUrl}}/v{{version}}/items?quantity=<10&warehouse=AALTR,AIPM&status=!CLOSED
| Parameter | Description | Example | Applicable Resource |
|---|---|---|---|
| ts | Timestamp for modified records (ISO 8601) | 2025-10-01T08:11:05.129Z | All resources |
| date | Date filtering (supports operators) | >2025-09-01T00:00:00 | Orders, Invoices |
| number | Document number | 12345 or !001 | Orders, Invoices |
| code | Item/Customer code | 9810 or CUST001 | Items, Customers |
| name | Name search | Kalev | Items, Customers |
| status | Status filter | UUS, CLOSED, NEW | Orders, Items |
| country | Country code (ISO 2-letter) | EE, !EE | Items, Customers |
| warehouse | Warehouse/stock location code | AALTR,AIPM | Items, Stock Levels, Orders |
| quantity | Quantity filter (supports operators) | >100, <50 | Items, Stock Levels |
| row_item | Item code in line items | 9810 or PROD-ABC | Orders, Invoices |
| closed | Closed status | 1 (closed), 0 (open) | Customers, Orders |
Problem: Filter returns no results
Problem: Filter seems to be ignored
Problem: Unexpected results
API Resources and Endpint can be found in Postman collection. Response field list managed in Directo ERP.
Postman collection can be downloaded here:POSTMAN
JSON Format:
[
{
"code": "1011",
"class": "KAUP",
"name": "Kommid Lily, Kalev",
"price": 12.50,
"quantity": 150,
"warehouse": "AALTR",
"datafields": [
{
"code": "TEST_ARTIKKEL",
"content": "Test lisavΓ€li sisu"
},
{
"code": "IS_WEIGHT",
"content": "0"
},
{
"code": "VISUAL_VERIFICATION",
"content": "0"
},
{
"code": "VENSAFE",
"content": "0"
}
]
},
{
"code": "1012",
"class": "KAUP",
"name": "Ε okolaad Kalev",
"price": 8.75,
"quantity": 200,
"warehouse": "AIPM",
"datafields": []
}
]
XML Format:
<items> <item code="1011" class="KAUP" name="Kommid Lily, Kalev" price="12.50" quantity="150" warehouse="AALTR"> <datafields> <data code="TEST_ARTIKKEL" content="Test lisavΓ€li sisu"/> <data code="IS_WEIGHT" content="0"/> <data code="VISUAL_VERIFICATION" content="0"/> <data code="VENSAFE" content="0"/> </datafields> </item> <item code="1012" class="KAUP" name="Ε okolaad Kalev" price="8.75" quantity="200" warehouse="AIPM"> <datafields/> </item> </items>
| Status Code | Meaning | Description |
|---|---|---|
| 200 | OK | Request successful |
| 400 | Bad Request | Invalid parameters or malformed request |
| 400 | API not enababled | Resource not enabled for this API key |
| 401 | Unauthorized | Missing or invalid API key |
| 403 | Forbidden | API key lacks required permissions |
| 404 | Not Found | Resource or endpoint not found |
| 429 | Too Many Requests | Rate limit exceeded |
| 500 | Internal Server Error | Server-side error |
| 503 | Service Unavailable | API temporarily unavailable |
{
"error": {
"code": "INVALID_API_KEY",
"message": "The provided API key is invalid or expired",
"details": "Please check your API key and try again"
}
}
401 Unauthorized:
403 Forbidden:
400 Bad Request:
404 Not Found:
429 Too Many Requests:
500 Internal Server Error:
While specific rate limits depend on your account tier, follow these guidelines:
1. Use Timestamp Filtering for Sync Instead of fetching all records repeatedly, use the ts parameter:
GET {{baseUrl}}/v{{version}}/items?ts>=2025-11-20T07:00:00Z
* Benefits: Reduces data transfer, Faster response times, Lower server load, Stays within rate limits
2. Implement Pagination with Date Ranges For large datasets, break requests into smaller chunks:
# Week 1
GET {{baseUrl}}/v{{version}}/orders?date=>2025-11-01&date=<2025-11-08
# Week 2
GET {{baseUrl}}/v{{version}}/orders?date=>2025-11-08&date=<2025-11-15
3. Cache Static Data Cache data that changes infrequently:
4. Use Specific Filters Apply filters to retrieve only the data you need:
# Instead of fetching all items and filtering locally
GET {{baseUrl}}/v{{version}}/items
# Fetch only what you need
GET {{baseUrl}}/v{{version}}/items?warehouse=AALTR&quantity=>0
5. Implement Exponential Backoff For transient errors (429, 503), retry with increasing delays:
6. Batch Related Requests Group related operations to minimize round trips:
1. Fetch orders: ''GET /orders?ts=>{last_sync}''
2. For each order, fetch customer details (if needed)
3. Update local database in batch
7. Monitor API Usage Track your API consumption:
Objective: Load all data from Directo ERP into your system
GET /itemsGET /customersGET /stocklevelsGET /itemclassestimestamp = current_time()Considerations: May take several minutes, run during off-peak hours, handle timeouts.
Objective: Keep your system up-to-date with changes in Directo ERP
GET /items?tsβ{last_sync}GET /customers?tsβ{last_sync}GET /stocklevels?tsβ{last_sync}GET /deleted?tsβ{last_sync}Objective: Monitor new orders for immediate processing
GET /orders?status=NEW&tsβ{last_check}Objective: Maintain accurate inventory availability
GET /stocklevels?item_code={sku}GET /allocations?item_code={sku}available = stock_quantity - allocated_quantityObjective: Keep customer data synchronized between Directo and your CRM
GET /customers?ts={last_sync}GET /deleted?resource_type=customers&ts={last_sync}Objective: Export invoices to external accounting software
GET /invoices?tsβ{last_sync}Objective: Keep product prices updated in your online store
GET /items?tsβ{last_sync}GET /priceformulas?tsβ{last_sync}Possible Causes: Filters too restrictive, incorrect date format, filtering by calculated field. Solutions:
# Remove filters one by one GET /items # Verify date format β CORRECT: ?date=>2025-11-20T00:00:00Z
Possible Causes: Missing key, incorrect header name, expired key. Solutions:
# Verify header name (case-sensitive) β CORRECT: X-Directo-Key: YOUR_KEY
Solutions (Python):
import time def make_request_with_retry(url, max_retries=5): for attempt in range(max_retries): response = requests.get(url) if response.status_code == 200: return response elif response.status_code == 429: wait_time = 2 ** attempt time.sleep(wait_time) raise Exception("Max retries exceeded")
Technical Support:
Version 1 (Current)
GET /items?code=1011Example (Python):
# Python example with comprehensive error handling import requests import time def fetch_directo_data(endpoint, params=None, max_retries=3): url = f"{{{{baseUrl}}}}/v{{{{version}}}}/{endpoint}" headers = { "X-Directo-Key": "YOUR_API_KEY", "Accept": "application/json" } for attempt in range(max_retries): try: response = requests.get(url, headers=headers, params=params, timeout=30) if response.status_code == 200: return response.json() elif response.status_code == 429: # Rate limit - wait and retry wait_time = 2 ** attempt time.sleep(wait_time) elif response.status_code in [401, 403]: # Auth error - don't retry raise Exception(f"Authentication error: {response.status_code}") else: # Other error - log and retry print(f"Error {response.status_code}: {response.text}") time.sleep(1) except requests.exceptions.Timeout: print(f"Timeout on attempt {attempt + 1}") time.sleep(2) except requests.exceptions.RequestException as e: print(f"Request failed: {e}") time.sleep(2) raise Exception(f"Failed after {max_retries} attempts") # Usage items = fetch_directo_data("items", params={"warehouse": "AALTR", "quantity": ">0"})
Last Updated: 29-01-2026 API Version: 1
Items: GET /v1/items Customers: GET /v1/customers Orders: GET /v1/orders Invoices: GET /v1/invoices Stock: GET /v1/stocklevels Deletions: GET /v1/deleted
X-Directo-Key: YOUR_API_KEY
Accept: application/json (or application/xml)
Exact match: ?field=value Greater than: ?field=>value Less than: ?field=<value Not equal: ?field=!value Multiple values: ?field=value1,value2 Timestamp: ?ts=2025-11-20T07:00:00Z
?warehouse=AALTR ?quantity=>100 ?date=>2025-11-01T00:00:00&date=<2025-12-01T00:00:00 ?status=NEW,PENDING ?country=!EE ?ts=2025-11-20T07:00:00Z
This documentation is maintained by the Directo development team. For questions or feedback, contact info@directo.ee