What it is
Every company in OptiRoutes has its own private folder of files: the orders it loads, the signed delivery notes (POD) with their photos and signatures, the route exports and the end-of-day reports. The Files API lets your systems read and write that folder directly, with no FTP server in between.
- Address:
https://srv2.optiroutes.com/api/v1— HTTPS only. - Access: an API key that the company's manager creates in OptiRoutes, in Settings → API & files. No request to OptiRoutes support is needed.
- Every file type the system works with today: CSV, JSON, TXT, XLSX/XLS, PDF and images.
- Answers are JSON, except file downloads, which return the file itself.
Start in 3 steps
- Create a key. In OptiRoutes, the manager opens Settings → API & files, clicks New key, gives it a name (for example "ERP server"), picks the permissions and copies the key. The key is shown once.
- Check the connection.
curl -H "Authorization: Bearer $OPTIROUTES_KEY" https://srv2.optiroutes.com/api/v1/me - Download today's signed PODs as one ZIP:
curl -H "Authorization: Bearer $OPTIROUTES_KEY" -o pod.zip "https://srv2.optiroutes.com/api/v1/pod/archive?date=$(date +%F)"
Security — please read
- Treat the key like a password. Keep it in a secret store or an environment variable on your server. Never put it in a web page, a mobile app, an e-mail, a ticket or source control.
- Least privilege. Give each system its own key with only the permissions it needs:
pod:read(signed PODs only),files:read(read every file of the company),files:write(upload orders and inbox files). - Limit the addresses. When your server has a fixed IP address, add it to the key ("Allowed addresses"). A call from any other address is refused.
- Expiry and rotation. A key can expire after a number of days. To rotate: create a new key, switch your system to it, then revoke the old one — there is no downtime.
- Revoke at once a key that may have leaked: Settings → API & files → Revoke. It stops working within 30 seconds.
- Audit. Every call is logged (time, address, file, answer) and shown to the manager in the same screen.
- OptiRoutes keeps only a fingerprint (SHA-256) of each key, never the key itself. Connections are HTTPS (TLS 1.2+) only.
- A key reaches only its own company's folder. Other companies' files and the system's internal folders can never be reached.
Authentication
Send the key in every request, in one of these headers:
Authorization: Bearer opr_2813_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# or
X-API-Key: opr_2813_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxA key looks like opr_2813_… (the company number, then 32 random characters).
The company folder
| Folder | What is in it | Who writes |
|---|---|---|
orders_files/ | The daily orders file that OptiRoutes loads: orders_files/YYYY-MM-DD.csv (or .json / .TXT, as set in Settings → Loading). Route exports: orders_files/routes_export_YYYY_MM_DD.csv. | You (orders) · OptiRoutes (exports) |
pod/YYYY/MM/DD/ | Signed PODs of that day: <order>_sign_….pdf, the signature and the photos (<order>_….png/jpg). | OptiRoutes |
pod/ | Source delivery notes to be signed in the driver app: pod/<order number>.pdf. | You |
reports/YYYY/MM/ | End-of-day and returns reports. | OptiRoutes |
podBaseFile/ | The POD base form. | You |
inbox/ | Free folder for files you want to hand over. | You |
Reference
| Call | Permission | What it does |
|---|---|---|
GET /api/v1/me | any | Checks the key. Answers the company number, the key's permissions and your address. |
GET /api/v1/files?folder=<folder> | files:read (pod:read: pod/ only) | Lists one folder level: files (name, path, size, modified) and sub-folders. |
GET /api/v1/files/download?path=<file> | files:read (pod:read: pod/ only) | Downloads one file. |
PUT /api/v1/files?path=<file> | files:write | Uploads a file (the request body is the file, or a multipart form with a file field). An existing file is replaced. At most 50 MB. |
DELETE /api/v1/files?path=<file> | files:write | Deletes a file you uploaded (where you may upload). |
GET /api/v1/pod?date=YYYY-MM-DD[&order=<number>] | pod:read | The POD files of a day, each with its order number and kind: signed_pdf, signature, photo. |
GET /api/v1/pod/archive?date=YYYY-MM-DD[&kind=signed_pdf] | pod:read | Every POD file of the day as one ZIP. |
Example answer of a listing:
{
"ok": true,
"folder": "pod/2026/10/11/",
"items": [
{ "name": "12345_sign_2026_10_11.pdf", "path": "pod/2026/10/11/12345_sign_2026_10_11.pdf",
"type": "file", "size": 84213, "modified": "2026-10-11T09:14:02.000Z" }
]
}Common tasks
Upload today's orders file
# curl (Linux / macOS / Windows 10+)
curl -X PUT -H "Authorization: Bearer $OPTIROUTES_KEY" \
--data-binary @orders.csv -H "Content-Type: text/csv" \
"https://srv2.optiroutes.com/api/v1/files?path=orders_files/$(date +%F).csv"
# PowerShell
Invoke-RestMethod -Method Put -InFile .\orders.csv -ContentType 'text/csv' `
-Headers @{ Authorization = "Bearer $env:OPTIROUTES_KEY" } `
-Uri "https://srv2.optiroutes.com/api/v1/files?path=orders_files/$(Get-Date -f yyyy-MM-dd).csv"OptiRoutes loads the file as it loads it today (the automatic loading of Settings → Loading, or the user's "Load file").
Download the signed PODs of one order
curl -H "Authorization: Bearer $OPTIROUTES_KEY" "https://srv2.optiroutes.com/api/v1/pod?date=2026-10-11&order=12345"
# then, for each item:
curl -H "Authorization: Bearer $OPTIROUTES_KEY" -OJ "https://srv2.optiroutes.com/api/v1/files/download?path=pod/2026/10/11/12345_sign_2026_10_11.pdf"
Download the end-of-day report
curl -H "Authorization: Bearer $OPTIROUTES_KEY" "https://srv2.optiroutes.com/api/v1/files?folder=reports/2026/10/"
curl -H "Authorization: Bearer $OPTIROUTES_KEY" -OJ "https://srv2.optiroutes.com/api/v1/files/download?path=reports/2026/10/report_2026_10_11.csv"
Python
import os, datetime, requests
BASE = "https://srv2.optiroutes.com/api/v1"
H = {"Authorization": f"Bearer {os.environ['OPTIROUTES_KEY']}"}
# upload today's orders
day = datetime.date.today().isoformat()
with open("orders.csv", "rb") as f:
r = requests.put(f"{BASE}/files", params={"path": f"orders_files/{day}.csv"},
data=f, headers={**H, "Content-Type": "text/csv"}, timeout=60)
r.raise_for_status()
# download every signed POD of the day
pods = requests.get(f"{BASE}/pod", params={"date": day}, headers=H, timeout=60).json()["items"]
for p in pods:
if p["kind"] == "signed_pdf":
data = requests.get(f"{BASE}/files/download", params={"path": p["path"]}, headers=H, timeout=60)
open(p["name"], "wb").write(data.content)
C# (.NET)
using var http = new HttpClient { BaseAddress = new Uri("https://srv2.optiroutes.com/api/v1/") };
http.DefaultRequestHeaders.Authorization =
new("Bearer", Environment.GetEnvironmentVariable("OPTIROUTES_KEY"));
// upload today's orders
var day = DateTime.Today.ToString("yyyy-MM-dd");
using var content = new ByteArrayContent(File.ReadAllBytes("orders.csv"));
content.Headers.ContentType = new("text/csv");
(await http.PutAsync($"files?path=orders_files/{day}.csv", content)).EnsureSuccessStatusCode();
// the day's PODs as one ZIP
var zip = await http.GetByteArrayAsync($"pod/archive?date={day}");
File.WriteAllBytes($"pod-{day}.zip", zip);
Node.js
const BASE = 'https://srv2.optiroutes.com/api/v1';
const H = { Authorization: `Bearer ${process.env.OPTIROUTES_KEY}` };
const day = new Date().toISOString().slice(0, 10);
// list and download the day's signed PODs (Node 18+)
const { items } = await (await fetch(`${BASE}/pod?date=${day}`, { headers: H })).json();
for (const p of items.filter((i) => i.kind === 'signed_pdf')) {
const res = await fetch(`${BASE}/files/download?path=${encodeURIComponent(p.path)}`, { headers: H });
await require('fs/promises').writeFile(p.name, Buffer.from(await res.arrayBuffer()));
}Work like FTP — a folder sync script
Teams that worked with FTP can keep a folder on their own server in sync with the company folder: a scheduled task downloads new files (for example the PODs) and uploads the files you drop in a local folder. The PowerShell script below does both; run it every few minutes from Windows Task Scheduler (on Linux use the curl lines with cron).
# optiroutes-sync.ps1 — download new PODs, upload what is in .\outbox (run every 5 minutes)
$Base = 'https://srv2.optiroutes.com/api/v1'
$H = @{ Authorization = "Bearer $env:OPTIROUTES_KEY" }
$Local = 'C:\OptiRoutes'
New-Item -ItemType Directory -Force "$Local\pod", "$Local\outbox", "$Local\sent" | Out-Null
# 1. today's and yesterday's PODs -> C:\OptiRoutes\pod\YYYY-MM-DD\
foreach ($d in (Get-Date), (Get-Date).AddDays(-1)) {
$day = $d.ToString('yyyy-MM-dd'); $dir = "$Local\pod\$day"
New-Item -ItemType Directory -Force $dir | Out-Null
$list = Invoke-RestMethod -Headers $H -Uri "$Base/pod?date=$day"
foreach ($f in $list.items) {
$dest = Join-Path $dir $f.name
if (-not (Test-Path $dest)) {
Invoke-WebRequest -Headers $H -OutFile $dest -Uri ("$Base/files/download?path=" + [uri]::EscapeDataString($f.path))
}
}
}
# 2. every file in C:\OptiRoutes\outbox -> orders_files/, then moved to C:\OptiRoutes\sent
Get-ChildItem "$Local\outbox" -File | ForEach-Object {
Invoke-RestMethod -Method Put -Headers $H -InFile $_.FullName -ContentType 'application/octet-stream' `
-Uri ("$Base/files?path=" + [uri]::EscapeDataString("orders_files/" + $_.Name)) | Out-Null
Move-Item $_.FullName "$Local\sent\" -Force
}Moving a company from FTP to the API
The manager switches in Settings → API & files → Files move by: API. From that moment nothing is sent to the company's FTP, and every file is in the company folder instead:
| With FTP | With the API |
|---|---|
| You put the orders file on the FTP | PUT /api/v1/files?path=orders_files/YYYY-MM-DD.csv |
Signed PODs pushed to /POD/ on your FTP | GET /api/v1/pod?date=… / /api/v1/pod/archive?date=… |
Route export pushed to /orders/ | orders_files/routes_export_YYYY_MM_DD.csv |
Reports pushed to /orders/YYYY/MM/ | reports/YYYY/MM/ |
New companies work with the API from the start. Switching back to FTP is the same switch.
Companies on the old OptiRoutes server
A company that still works with the old OptiRoutes server (WEB) can use the API for its signed PODs: create a key with the pod:read permission and use /api/v1/pod, /api/v1/pod/archive and /api/v1/files/download?path=pod/…. Its other files keep moving as they do today, until the company moves to the new server.
Errors
An error answers {"ok": false, "error": "<code>", "message": "<explanation>"}.
| HTTP | error | What to do |
|---|---|---|
| 401 | missing_key / invalid_key | Send the key; check it was not revoked or expired. |
| 403 | scope_missing | The key lacks the permission — create a key with it. |
| 403 | ip_not_allowed | Call from an allowed address, or update the key's addresses. |
| 403 | folder_read_only | Upload only to orders_files/, inbox/, podBaseFile/ or pod/<file>.pdf. |
| 400 | invalid_path / invalid_date | Paths are relative (orders_files/a.csv), dates are YYYY-MM-DD. |
| 404 | not_found / no_pod_files | No such file / no POD that day. |
| 413 | file_too_large | At most 50 MB a file. |
| 429 | rate_limited | Wait a minute and retry. |
| 502 / 503 | storage_unavailable / files_api_unavailable | Temporary — retry in a minute. |
Limits
- 300 requests a minute per key.
- 50 MB a file.
- 20 active keys per company.
- A folder listing returns up to 5,000 entries; list sub-folders (for example one day of PODs) for more.
Help
The company's manager sees every key, its last use and the last calls in Settings → API & files. For anything else, contact OptiRoutes support with the company number and the time of the call — never send the key itself.