This page provides details of a typical integration and suggestions on how to keep data synchronized with VOCUS.
Both VOCUS and FlightRisk will be discussed. Note that VOCUS is a platform comprised of multiple products and services, and FlightRisk is a risk assessment app that is part of the VOCUS suite.
In this example, we'll consider a Software as a Service (SaaS) aviation scheduling app. As a SaaS app, it will contain data for multiple companies. Each company will have its own service account connected to a domain in VOCUS.
We'll assume that customers will opt-in to using VOCUS and will be configured on demand, rather than all customers at once.
Our app will need:
Service accounts are configured by Polaris Aero during the onboarding process for a customer. Once service account credentials are obtained, they can be used to make API calls for that customer.
Most VOCUS APIs are idempotent other than logging the action. This means it is safe to make repeated API calls if there's a chance a sync is needed.
However, an automated process should not blindly sync the same data repeatedly, especially if a failure occurs. An HTTP 5xx
error indicates a temporary condition,
and retrying it within hours or even minutes makes sense. But an HTTP 400
indicates that the request failed validation. Repeatedly submitting the same invalid request will have no effect
other than cluttering the history for the domain within VOCUS and consuming resources in both applications.
Sync logs, especially errors, should be available to users to assist in troubleshooting. All syncing should be automatic, but controls to manually sync/re-sync data to VOCUS are helpful to users.
The design should not impede user flow or slow down the UI, so it should run in the background. There are two main approaches that can be taken:
This approach uses a database table called VocusSyncHistory
to track when object changes were last synced with VOCUS.
It requires tracking of changes within the app. All changes to an object must update a LastModifiedDate
field and/or history table, and there must also be a record of deleted objects so that the deletes can be sent to VOCUS.
Queries to this table should only return changes made since the last successful sync with VOCUS, but since failures are possible, the success of each object should be tracked individually. Otherwise, a single failure could cause the update batch to grow endlessly, leading to poor performance and unnecessary sync traffic.
Column | Type | Purpose |
ObjectId | int | The ID of the object being synced. |
ObjectTypeId | enumeration | A value that corresponds to the type of object (e.g. account, tail, trip, leg, etc.) |
Date | dateTime | The time of the last sync attempt. |
AttemptCount | integer | The number of attempts made. |
Debug | string | Records the last warnings or errors returned from VOCUS. |
Using a scheduled job/task, periodically check for changes to the app's internal tables for changes.
Compare that to the VocusSyncHistory
table to find items which have eligible changes since they were last synced, or items
which haven't successfully synced and meet the retry criteria.
After a sync operation, update the VocusSyncHistory
table.
If the request failed, increment the AttemptCount
column. This value can be used in a query to delay/cancel future attempts to sync this item.
If a failure has occurred repeatedly, the app should notify an application administrator and/or power user that a failure has occurred.
This query, written in T-SQL, that shows how a history table can be used to find pending leg changes. Your actual query will likely have multiple
select
statements to return both trips and legs that have changed, and to return deleted trips and legs.
-- Set a minimum date to avoid syncing old data on an initial sync. declare @minDate datetime = dateadd(month, -6, getutcdate()); -- This value allows filtering of other object types tracked in the same history table. declare @legObjectTypeId tinyint = 4; select * from ( select l.LegId, isnull(vsh.AttemptCount, 0) as AttemptCount, max(tai.Date) as LastModifiedDate, max(vsh.Date) as LastSyncDate -- your app's Leg table from schedule.Leg l -- your app's Leg history table join audit.TripAuditItem tai on l.LegId = tai.LegId -- history table from example left outer join dbo.VocusSyncHistory vsh on vsh.ObjectId = l.LegId and vsh.ObjectTypeId = @legObjectTypeId group by l.LegId, vsh.AttemptCount having max(tai.Date) > @minDate ) q1 -- where the leg has never been synced, or hasn't been synced since the most recent update where (q1.LastSyncDate is null or q1.LastModifiedDate > q1.LastSyncDate) -- filter out records with numerous failures and q1.AttemptCount < 5;
A queue can be as simple as a database table or a more robust commercial solution such as Azure Service Bus. As edits or deletes are made within the app, they should be pushed in order to the queue.
Remember to capture all changes. For example, if moving a leg on the schedule changes the departure time of another leg, both legs should be queued.
Use the Account and Tail APIs to send user accounts and tails to VOCUS.
It is necessary to sync user accounts and tails to VOCUS so they can be referenced by their mapped IDs when trips are synced and risk assessments are run. By design, there are not many account/tail fields that can be synced.
If run on a timer, this sync should be run every 4-12 hours.
If an account has never been successfully synced, attempt to create it. Remember, the account may already exist from another source or manual setup, so check the HTTP status code that is returned.
If the status code is HTTP 201
, you should grant access
to FlightRisk, since the user will need FlightRisk access to see the schedule data we are going to sync.
Once the account has been created, it does not need to be synced unless its status (active/inactive) changes.
If a tail has never been successfully synced, attempt to create it.
Once the tail has been created, it does not need to be synced unless its tail number or status (active/inactive) changes.
Use the FlightRisk Schedule API to send trips, legs, and duty data to VOCUS.
Once users and tails have been synced to VOCUS, schedule data can be sent. Unlike users and tails, changes to trips and legs need to be sent to VOCUS in near real-time.
Most data is sent via the leg, not the trip. A typical scheduling app will have all the required fields, with one exception: the rulesetId
field.
Rulesets are defined per customer within VOCUS, and a list can be obtained by calling the Ruleset API.
Ideally, your app will provide a simple UI that allows customers to map their leg types to rulesets.
If a periodic sync approach is used, it should be run every 5-15 minutes. Don't allow overlapping execution; new processing should not begin if the prior execution is still running.
The sequence of API calls may be important. For example, making a call to delete a trip will delete all its legs within VOCUS.
There's no need to make separate calls to delete those legs, and VOCUS will return HTTP 400
because the legs can no longer be located by their mapped IDs.
Suggested sequence:
If using a queue-based approach, all edit and delete events need to be sent to the queue. Ensure that the current state of the object (trip or leg) is passed to the queue, not just its ID. This will provide better performance and more accurate history tracking if an object changes multiple times before the queue is processed.
The queueing solution should support retrying failed operations, and should support eventually dead-lettering failed items after reaching a retry threshold.
API Version 14.1.9203.25249
Copyright ©2008-2025 Polaris Aero, LLC.