API Gateway
Overview
The API Gateway is an open-source MIT-licensed middleware (built on Netflix Zuul) that lets end users, mobile apps, and web pages talk to Orchestra in a controlled and secure way. It sits in front of Orchestra Central — typically deployed in the customer's DMZ — and acts as a single front door for external traffic, so you never have to expose Orchestra Central directly to the internet.
The Gateway also caches frequently-accessed responses, reducing the load on Orchestra Central. That extra throughput is what makes large-scale Mobile Ticket and similar deployments practical.
The API Gateway is not recommended for communication between internal applications. It is designed to gate traffic from external clients (mobile apps, public web pages, third-party integrations).
Use cases
- Mobile Ticket and custom mobile apps — issue virtual tickets, look up the current status of a visit, get the position in the queue, list nearby branches.
- Geo-location lookups — find branches within a radius of a coordinate, or the nearest branches to a coordinate, without putting that load on Orchestra Central.
- Public web booking — expose a curated subset of Orchestra REST endpoints to a browser-based booking flow, with API-token authentication and request caching in front.
- Third-party / partner integrations — give a partner restricted access via an API token, without giving them an Orchestra account.
When not to use the API Gateway
- Internal service-to-service traffic inside your data centre — talk to Orchestra directly.
- Use cases that require write-heavy, real-time interaction with Orchestra's full admin surface — the Gateway exposes a deliberately narrow set of endpoints.
Architecture
[ Mobile app / Web page ] DMZ Internal network
│
│ HTTPS ───────► [ API Gateway ] ───────► [ Orchestra Central ]
│ (Zuul + cache) (REST connectors)
External clients hit the API Gateway over HTTPS using an API token (auth-token header). The Gateway authenticates the request, optionally serves it from cache, and otherwise proxies it to Orchestra Central using a configured Orchestra user account. Responses can be cached on the way back so the next identical request is served without touching Orchestra.
Components
- Client — mobile app, web page, or partner integration. Sends requests with an API token.
- API Gateway — Zuul-based proxy, runs as a service on Windows or Linux. Holds the configuration, the token store, the route table, and the cache.
- Orchestra Central — the backend. The Gateway authenticates against a dedicated user (or the Mobile user for the Mobile connector).
application.yml— the central configuration file under<install>/conf/. Defines tokens, routes, cache rules, CORS, SSL, checksum validation.ehcache.xml— cache definitions (TTLs per cache name) thatapplication.ymlrefers to by name.- Override directories — optional
.ymlfiles in/etc/apigw.d(Linux) orc:\qmatic\apigw.conf(Windows), or anywhere pointed to byAPI_GW_SYSTEM_CONFIG_PATH/API_GW_APPLICATION_CONFIG_PATH. Used to keep environment-specific settings out of the defaultapplication.yml.
Prerequisites
Before starting:
- Orchestra version: 6.1 build 3.1.0.489 or later, or Orchestra 6.0 build 3.0.0.537 or later. Newer Orchestra releases (7.x) are also supported — confirm against the API Gateway release notes for the version you intend to install.
- Server hardware (recommended for high load): 7 GB RAM, 4-core CPU, Windows Server. Linux servers with similar specs work.
- Java: JRE 8 is bundled with the API Gateway zip — no separate install required. Recent Gateway releases ship Zulu OpenJDK builds (e.g.
jre 1.8.0_432-b06in 1.5.8.0+). - Network: inbound port
9090open on the Gateway host. Outbound access from Gateway to Orchestra Central on Orchestra's port (typically 8080 HTTP or 8443 HTTPS). - Licensing: because the Gateway calls Orchestra connectors, your Orchestra licence must include the connectors you plan to expose. See the Order and Orchestra sections on Qmatic World.
Version handling
The API Gateway version number has four parts: <major>.<minor>.<bug>.<customisation>. If you build a customised version, increment the fourth digit (e.g. 1.3.4.0 → 1.3.4.1). It helps Support trace problems back to your build.
Installation
Windows
- Copy
QP_APIGateway_win-<version>.zipto the target machine and unzip it to a suitable folder (Java 8 is bundled — no separate install needed). - In
QP_APIGateway_win-<version>\bin, right-clickinstall-api-gw-service.batand select Run as administrator. This installs a Windows service calledQmatic API Gateway. - Start the
Qmatic API Gatewayservice if the install script didn't start it automatically.
The Gateway can run on a separate server from Orchestra Central — that's the typical DMZ deployment pattern.
Linux
sudo yum install unzip
# copy QP_APIGateway_linux64-<version>.zip into /tmp first
sudo adduser qmatic
sudo mkdir -p /opt/qmatic/QP_APIGateway
sudo chown -R qmatic:qmatic /opt/qmatic
sudo su - qmatic
cd /tmp
unzip QP_APIGateway_linux64-<version>.zip -d /opt/qmatic/QP_APIGateway
cd /opt/qmatic/QP_APIGateway/bin
nano api-gateway-service
Edit api-gateway-service and set the install paths:
gwUser="qmatic"
gwHome="/opt/qmatic/QP_APIGateway/"
Then install the systemd service and edit the main config:
./install-api-gateway-service.sh
cd /opt/qmatic/QP_APIGateway/conf
nano application.yml
In application.yml, set the API token, the Orchestra host URL, and the connector passwords (see Authentication and Configuration).
Authentication
The Gateway uses API tokens for client authentication and encrypted Orchestra passwords for backend authentication. Both are generated by helper scripts shipped in <install>/bin/.
Generate an API token
API tokens identify and separate different consumers (e.g. one token per app), give you per-token access control, and let you avoid handing out Orchestra credentials.
Run from <install>/bin/:
- Windows:
api-token-generator.bat - Linux:
./api-token-generator
Run it from a Command Prompt / shell so you can read the generated token. Make a note of it — you'll paste it into
application.ymlnext.
Create an encrypted Orchestra password
Don't put plain-text passwords in application.yml. Run:
- Windows:
password-encoder.bat - Linux:
./password-encoder
The script prints the encrypted password. Copy that into application.yml.
Configure connector users in Orchestra
The Gateway authenticates to Orchestra differently depending on which connector it's calling.
Normal connector user (everything except the Mobile connector). In Orchestra's User Management:
- Create a new role (e.g.
API User). In the role's Connectors panel, tick exactly the connectors you want the Gateway to be able to use — this is the surface area you're willing to expose. - Create a new user (e.g.
gwuser) and assign that role. Use a new user — Orchestra doesn't allow renaming an existing one. Note the password; you'll encrypt and paste it intoapplication.yml.
Mobile connector user (only if exposing the Mobile connector). In System Administration → Parameters → Mobile API (Central), change the password of the built-in mobile user. Save.
If you're using the Mobile connector, also add the VisitApp unit type:
- System Administration → Unit Types — add
VisitApp. - Business Configuration → Equipment Profile — add it to the relevant equipment profiles, both at profile and branch level.
See the Standard Unit Types Guide on Qmatic World for the full unit-type configuration.
Branches and services
In Business Configuration, every branch and service that should be reachable through the Gateway must have:
- Mobile enabled ticked.
- For branches: Longitude and Latitude filled in (required for
/geo/*calls).
Firewall
Open inbound port 9090 on the Gateway host. On Windows: Control Panel → Windows Firewall → Advanced Settings → Inbound Rules → New Rule → Port → 9090.
Configuration
All Gateway configuration lives under <install>/conf/:
application.yml— main configuration (tokens, routes, cache, CORS, SSL).application_MobileTicket.yml— pre-baked configuration for Mobile Ticket-only deployments. Use this instead ofapplication.ymlif Mobile Ticket is the only thing you're fronting. See the Mobile Ticket Manual on Qmatic World.logbackAPIGateway.xml— log configuration (same shape as Orchestra'slogback.xml).ehcache.xml— cache definitions referenced by name fromapplication.yml.
Hot reload — saving
application.ymlreapplies the configuration on the fly. No restart needed.Don't use tab characters when editing
application.yml. YAML rejects them.
API tokens and encrypted passwords
Paste the generated token and encrypted password into the api_tokens block. Each entry maps a token to the Orchestra user it should authenticate as:
orchestra:
api_tokens:
c7a1331a-32d-11e5-bf7f-feff819acdc9f:
user: gwuser
passwd: dWxhbg==
d0516eee-a32d-11e5-bf7f-feff819cdc9f:
user: mobile
passwd: dWxhbg==
Remove the default
superadminentry on anything other than a test system.
HTTPS / SSL
There are two SSL-capable hops: from the client to the Gateway, and from the Gateway to Orchestra Central. They can be configured independently — for example, HTTP from app to Gateway and HTTPS from Gateway to Orchestra is a valid combination.
The
management.sslsetting must befalse, otherwise the shutdown command from the service script doesn't work.
HTTPS from clients to the Gateway
Place keystore.jks in <install>/conf/ and uncomment the ssl block under server: in application.yml:
server:
port: 9090
ssl:
key-store: classpath:keystore.jks
key-store-password: changeit
key-password: changeit
key-store-password must match the keystore password; key-password must match the key/certificate password. The default changeit is a placeholder — change it. Listening on both HTTP and HTTPS simultaneously isn't supported — pick one.
HTTPS from Gateway to Orchestra
Point the upstream URL at HTTPS:
orchestra:
central:
url: https://localhost:8443
If Orchestra's certificate isn't trusted by the JVM by default, import it into the Gateway's truststore and reference it:
trustStore: conf/truststore.jks
trustStorePass: changeit
To bypass certificate validation entirely, set disableSslCertificateChecks: true. Do not do this in production.
Routes
zuul.routes defines which Orchestra paths the Gateway proxies. Only the listed routes are reachable; everything else returns 404. Match the routes to the connectors you ticked on the Orchestra role:
zuul:
routes:
appointment_api:
path: /rest/appointment/**
url: ${orchestra.central.url}/qsystem/rest/appointment
entrypoint_api:
path: /rest/entrypoint/**
url: ${orchestra.central.url}/qsystem/rest/entrypoint
servicepoint_api:
path: /rest/servicepoint/**
url: ${orchestra.central.url}/qsystem/rest/servicepoint
mobile_service_v2:
path: /rest/mobile/v2/**
url: ${orchestra.central.url}/qsystem/mobile/rest/v2
calendar_public_api:
path: /rest/calendar-backend/public/api/**
url: ${orchestra.central.url}/calendar-backend/public/api
The full default application.yml includes routes for appointment_api, entrypoint_api, servicepoint_api, management_information, mobile_service_v1, mobile_service_v2, calendar_internal_api, calendar_public_api, plus MobileTicket/* aliases. Comment out the ones you don't need to keep your attack surface small.
Checksum validation
When a visit is issued through the Mobile connector, Orchestra returns a checksum alongside the visit ID. The Gateway can require that checksum on subsequent calls — without it, an attacker who guesses visit IDs could remove someone else's place in a queue.
Checksum validation is enabled by default. To disable it for testing:
enableChecksum: false
When enabled, name the routes it applies to and the path parameter that holds the visit ID:
checksumRoutes:
my_visit_delete:
parameter: ticket
my_visit_current_status:
parameter: visits
CORS
CORS support is off by default. Configure it under http.cors.paths. Single-path form:
http:
cors:
paths:
path: /**
allowedOrigins: '*'
allowedHeaders: '*'
exposedHeaders: '*'
allowedMethods: GET, POST, PUT, OPTIONS, HEAD, DELETE, PATCH
allowCredentials: true
maxAge: 10000
Multi-path form (note the YAML list syntax):
http:
cors:
paths:
- path: /foo
allowedOrigins: http://foo.com, http://bar.com
allowedHeaders: X-Foo, X-Bar, Content-Type
exposedHeaders: X-Foo, X-Bar
allowedMethods: GET, POST, PUT, OPTIONS, HEAD, DELETE, PATCH
allowCredentials: true
maxAge: 3600
- path: /bar/**
allowedOrigins: http://bar.com
allowedMethods: GET
Per-attribute reference:
| Attribute | Required | Maps to | Notes |
|---|---|---|---|
path | yes | — | Path the rule applies to. Supports **. |
allowedOrigins | yes | Access-Control-Allow-Origin | Comma-separated list, or '*'. |
allowedHeaders | no | Access-Control-Allow-Headers | Comma-separated, or '*'. |
exposedHeaders | no | Access-Control-Expose-Headers | Comma-separated, or '*'. |
allowedMethods | no | Access-Control-Allow-Methods | Comma-separated, or '*'. |
allowCredentials | no | Access-Control-Allow-Credentials | Boolean. |
maxAge | no | Access-Control-Max-Age | Seconds. |
Orchestra also returns CORS headers, so you may end up with duplicates. To suppress Orchestra's:
zuul.ignored-headers: Access-Control-Allow-Credentials, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Access-Control-Expose-Headers, Access-Control-Max-Age, Access-Control-Allow-Methods
The active CORS configuration is logged on startup, e.g.:
Registered CORS config: CorsPath [path=/**, allowCredentials=true, maxAge=10000,
allowedOrigins=[http://foo.com, http://bar.com], allowedHeaders=[header1, header2],
exposedHeaders=[header3, header4], allowedMethods=[GET, POST, PUT]]
Override directories (recommended for production)
To keep environment-specific settings out of the binary distribution, drop additional .yml / .yaml files into one of two directories. The Gateway reads them on startup and they override application.yml:
- System-level (Linux):
/etc/apigw.d/ - System-level (Windows):
c:\qmatic\apigw.conf\ - Override the system-level location with the env var
API_GW_SYSTEM_CONFIG_PATH. - Instance-level: any directory pointed to by
API_GW_APPLICATION_CONFIG_PATH. Highest precedence — useful when co-hosting multiple Gateway instances on one box.
Files within a directory are applied in alphanumeric order (so 30-override-hystrix-threshold.yml overrides 20-override-api-keys.yml). Array-type settings (like orchestra.api_tokens) merge, so spreading tokens across multiple files is safe.
Final precedence, highest first:
- Files in
API_GW_APPLICATION_CONFIG_PATH. - Files in
/etc/apigw.d(Linux) orc:\qmatic\apigw.conf(Windows) — or whereverAPI_GW_SYSTEM_CONFIG_PATHpoints. - Default
conf/application.yml.
On startup the Gateway logs every file it picks up. Sensitive keys (api_tokens, password) are redacted from the active-config dump.
Cache
Request caching
The Gateway caches GET responses to take pressure off Orchestra Central. The first request for a given URI hits Orchestra; subsequent identical requests are served from cache until the entry expires.
1. Client → Gateway: GET /rest/...
2. Gateway: this is a cacheable GET; check cache.
3. Cache miss → forward to Orchestra Central.
4. Orchestra responds.
5. Gateway: store response in cache, return to client.
(next identical request from any client)
1. Client → Gateway: GET /rest/...
2. Gateway: cache hit → return immediately, Orchestra is not touched.
Cache configuration lives in two files:
application.yml— names the caches that apply to specific Zuul routes (cacheRoutes).ehcache.xml— defines per-cache TTL and size (referenced by name).
Example cache block:
cache:
allowCacheMetricsFromAllHosts: false
useDefaultCache: true
defaultCacheName: request
defaultCacheUniquePerQueryParameter: false
cacheRoutes:
services:
cacheName: serviceCache
match: /services
routes: mobile_service_v1, mobile_service_v2
uniquePerQueryParameter: false
visits:
cacheName: visitInQueueCache
match: /branches/[0-9]+/queues/[0-9]+/visits[/]?
routes: servicepoint_api
events:
cacheName: eventInQueueCache
match: /branches/[0-9]+/visits/[0-9]+/events[/]?
routes: my_visit_last_queue_event
uniquePerQueryParameter controls whether two requests with different query strings count as separate cache keys. Default false — useful when clients add cache-busting params like ?_=1351353151515 that you want to ignore for caching purposes. Set to true for endpoints like visit-status lookups, where the visit ID is in the query string and queries genuinely differ.
useDefaultCache: true means any GET request that doesn't match a cacheRoutes entry still gets cached using defaultCacheName (the cache named request in ehcache.xml).
Don't remove the
branchesandserviceBranchescaches fromehcache.xml— the GEO request-caching feature requires them. You can adjust their parameters, but the names must remain.The
visitsOnBranchCachedefinition hastimeToLiveSecondsset to 1 by default. Adjust it for your scenario before going live.
GEO request caching
The Gateway exposes a custom service at /geo that handles location-based mobile lookups. These calls require coordinate calculations, which the Gateway performs locally against cached branch and service data — Orchestra is hit only when the cache is cold.
Endpoints:
/geo/branches?longitude=…&latitude=…&radius=…— branches within radius (metres) of a point./geo/nearestbranches?longitude=…&latitude=…&maxNrOfBranches=…— closest N branches to a point./geo/services/{serviceId}/branches?…— branches offering a given service within a radius./geo/services/{serviceId}/nearestbranches?…— closest branches that offer a given service.
Example:
curl -i -X GET \
-H "auth-token: d0516eee-a32d-11e5-bf7f-feff819cdc9f" \
"http://localhost:9090/geo/branches?longitude=12.01795&latitude=57.63716&radius=1000"
Trial REST call
Once the Gateway is installed and application.yml is set up, do a smoke test through a tool like Postman or Advanced REST Client.
Issue a Mobile Ticket for service 1 at branch 1:
POST http://localhost:9090/MobileTicket/services/1/branches/1/ticket/issue
auth-token: <your-api-token>
The response should include clientId, serviceId, branchId, queueId, ticketNumber, visitId, and checksum. If you get a 401 or 404, re-check the API token, the encrypted password, the Orchestra user role, and that the requested branch/service has Mobile enabled ticked.
Mobile Ticket endpoints
The API Gateway exposes a curated set of endpoints under /MobileTicket/... that wrap the underlying Orchestra Mobile connector calls and apply the right caching and checksum rules:
| Operation | Path |
|---|---|
| List branches for a service | GET /geo/services/{serviceId}/branches?… |
| List nearest branches for a service | GET /geo/services/{serviceId}/nearestbranches?… |
| Issue ticket | POST /MobileTicket/services/{serviceId}/branches/{branchId}/ticket/issue |
| Current visit status | GET /MobileTicket/MyVisit/CurrentStatus/branches/{branchId}/visits/{visitId}?checksum=… |
| Last queue event | GET /MobileTicket/MyVisit/LastEvent/branches/{branchId}/visits/{visitId}/events |
The legacy
/MobileTicket/MyVisit/Position/branches/{branchId}/queues/{queueId}/visits?visitId=…call is deprecated. Use/MobileTicket/MyVisit/CurrentStatus/...instead — it doesn't require the client to track which queue the visit ended up in.
Examples:
# List nearest branches for service 1
curl -i \
-H "auth-token: d0516eee-a32d-11e5-bf7f-feff819cdc9f" \
-X GET "http://localhost:9090/geo/services/1/nearestbranches?longitude=12.01795&latitude=57.63716&maxNrOfBranches=1000"
# Issue a Mobile Ticket
curl -i \
-H "auth-token: d0516eee-a32d-11e5-bf7f-feff819cdc9f" \
-X POST "http://localhost:9090/MobileTicket/services/1/branches/1/ticket/issue"
# Current status of a visit
curl -X GET \
-H "auth-token: d0516eee-23ef453" \
"http://localhost:9090/MobileTicket/MyVisit/CurrentStatus/branches/1/visits/47?checksum=1585286891"
# Last queue event for a visit
curl -i \
-H 'auth-token: c7a1331a-32d-11e5-bf7f-feff819acdc9f' \
-X GET "http://localhost:9090/MobileTicket/MyVisit/LastEvent/branches/1/visits/83/events"
Upgrade
Upgrade is not formally supported — any custom configuration applied to one version may need to be re-applied manually to the next, and feature behaviour can change between versions.
To make upgrades as painless as possible:
- Keep environment-specific configuration in override directories, not in the bundled
conf/application.yml. The upgrade then becomes a drop-in replacement of the binary. - Pin and document the API Gateway version in your operations runbook.
- Read the release notes between your current and target versions before upgrading — recent versions added a circuit breaker (1.4.8.0+, refined per-branch in 1.5.9.0), configurable timeouts on the visit-status path (1.5.10.0+), GZip support (1.4.4.0), and various JRE refreshes.
Troubleshooting
| Symptom | Likely cause | Resolution |
|---|---|---|
401 Unauthorized on every call | API token wrong, or the Orchestra user it maps to lost the connector role | Verify the token in application.yml, the encrypted password, and the role on the Orchestra user. Restart the Gateway after changes if not using hot-reload. |
404 Not Found with no body when querying visit status | Visit was already called and ended | Expected — the visit has left the queue. |
404 Not Found with a JSON body referencing the refresh rate | visitsOnBranchCache not yet refreshed | Wait one cache interval (default 1 second) and retry. Tune timeToLiveSeconds in ehcache.xml. |
| Gateway won't shut down via service script | management.ssl is set to true | Set management.ssl: false in application.yml. |
Duplicate Access-Control-* headers in responses | Orchestra and Gateway both emit CORS headers | Add zuul.ignored-headers: Access-Control-Allow-Credentials, Access-Control-Allow-Origin, … to suppress Orchestra's. |
| Non-Latin characters garbled in responses | Default JVM charset on host | Fixed in 1.3.5.0 (UTF-8 used unconditionally). Upgrade if older. |
getVisitStatusMap waits indefinitely on a busy branch | Pre-1.5.10.0 — no timeout on branchSemaphore | Upgrade to 1.5.10.0 or later (default 30 s timeout) or 1.5.10.1+ (timeout exposed in cloud config). |
| Circuit breaker tripping for all branches when one is slow | Pre-1.5.9.0 — single circuit shared across branches | Upgrade to 1.5.9.0+ (per-branch circuit). |
| Service won't start on Windows Server 2019 | Pre-1.5.7.0 — bundled curl requires vcredist | Upgrade to 1.5.7.0+ (curl no longer requires the redistributable). |
FAQ
Where do logs go?
<install>/log/api_gateway.log. Adjust paths and levels in <install>/conf/logbackAPIGateway.xml. The Gateway's version is written to the log at startup (since 1.4.8.0).
How do I add a new API token without restarting?
Add it to application.yml (or to a file in your override directory) and save. The Gateway picks up application.yml changes automatically — no restart needed. Token files in override directories merge with the defaults on startup, so new override files do require a restart.
Why is the cache hit rate lower than expected?
Likely defaultCacheUniquePerQueryParameter: true plus a client that adds cache-busters like ?_=<timestamp>. Set it to false (the default since 1.4.9.0) so query strings don't fragment the cache key, unless the query genuinely identifies different resources.
Can I run two API Gateway instances on the same host?
Yes. Use API_GW_APPLICATION_CONFIG_PATH to point each instance at its own override directory (different ports, different tokens, different routes if needed).
Related resources
- Mobile Ticket guide — the most common consumer of the Gateway.
- Mobile App Integration guide — patterns for custom mobile clients fronted by the Gateway.
- Orchestra API 7.4 reference — the underlying REST endpoints the Gateway proxies.