Keeping an external system in sync
This article describes the recommended approach for keeping an external system synchronised with Arlo. Most integrations need to pull data from Arlo into an external system, and many also need to push changes back. A robust integration uses complementary mechanisms for each direction:
Pulling from Arlo (detecting changes in Arlo and importing them):
- Webhooks — the primary mechanism for near real-time change notifications.
- PlatformEvents query — an optimised endpoint for batch-fetching entities referenced by webhook events.
Pushing to Arlo (writing changes from the external system into Arlo):
- Pushing changes into Arlo — creating and updating records when the external system is the source of truth for certain data.
For first-time setup or full data re-imports, see Initial data load and bulk synchronisation.
Topics:
- Synchronisation strategy overview
- Real-time sync with webhooks
- Fetching entity details from webhook events
- Initial data load and bulk synchronisation
- Pushing changes into Arlo
Synchronisation strategy overview
The mechanisms below serve different purposes. For pulling data from Arlo, use them together for a reliable integration. For pushing data into Arlo, the approach depends on whether you are creating or updating records:
| Mechanism | Direction | Use case | Latency |
|---|---|---|---|
| Webhooks | Pull | Primary change detection — notifies your system as soon as a resource is created or updated | Near real-time (seconds) |
| PlatformEvents query | Pull | Batch-fetch entity details for webhook events | On demand |
| Bulk collection queries | Pull | Initial data load, full re-import, or periodic audit of all records | Minutes to hours (depending on data volume) |
| HTTP PATCH / POST | Push | Update existing records or create new ones when the external system is the source of truth | On demand (event-driven or scheduled) |
For pulling data, webhooks should be the backbone of your integration. Bulk collection queries are appropriate for initial setup and occasional full resynchronisation, but should not be used as a substitute for webhooks in steady-state operation. Polling collection endpoints at regular intervals is inefficient — most requests return no new data — and introduces unnecessary delay.
For pushing data, use HTTP PATCH to update specific fields on existing records and HTTP POST to create new ones. If your integration operates in both directions, see Avoiding update loops for guidance on preventing feedback cycles.
Real-time sync with webhooks
Webhooks are the recommended way to detect changes in Arlo. When a resource is created or updated, Arlo sends a HTTP POST callback to your registered endpoint containing a JSON payload with one or more event references.
Your webhook handler should validate the callback, process the events, and track the highest event ID (high watermark) it has successfully processed.
See Webhooks for full documentation, including payload format, supported events, handler best practices, and signature validation.
Fetching entity details from webhook events
Webhook payloads contain only event references (resource type and ID) — not the entity details themselves. To retrieve the actual entity data, use the PlatformEvents query endpoint, which allows you to batch-fetch all referenced entities in a single request.
The typical flow is:
- Receive a webhook callback containing one or more events.
- Extract the event
idvalues from the payload. - Query the PlatformEvents endpoint with those IDs and expand the linked entities:
/api/2012-02-01/auth/resources/platformevents/query/?ids=108,109,110&expand=PlatformEvent,PlatformEvent/Link
This returns the full entity representations (Contacts, Registrations, Organisations, etc.) for all events in a single response, avoiding the need to query each resource endpoint individually.
See Fetching events and related entities as a batch on the Webhooks page and the PlatformEvents query endpoint for full documentation.
Initial data load and bulk synchronisation
For first-time setup, full data re-imports, or periodic audits, you can retrieve all records of a given resource type using cursor-based pagination on collection endpoints. This approach applies to all main resources including Contacts, Registrations, Organisations, Events, and Orders.
The examples below use the Contacts collection, but the same pattern works for any resource that supports LastModifiedDateTime ordering.
Two values need to be stored between queries to maintain your position in the result set:
LastModifiedDateTime— the modification timestamp of the last record in the current page.ContactID(or equivalent resource ID) — used as a tiebreaker when multiple records share the same timestamp.
First page
For the initial request, no cursor values are needed. Order by LastModifiedDateTime and resource ID ascending:
/api/2012-02-01/auth/resources/contacts/?orderby=LastModifiedDateTime ASC,ContactID ASC&expand=Contact&skip=0&top=100
This returns the first page of results (100 records).
Subsequent pages
Use the stored cursor values to fetch the next page. The filter retrieves records that were modified after the cursor timestamp, or records with the same timestamp but a higher ID (to handle ties):
/api/2012-02-01/auth/resources/contacts/?filter=LastModifiedDateTime gt datetime('2013-07-16T23:57:12.943Z') OR (LastModifiedDateTime eq datetime('2013-07-16T23:57:12.943Z') AND ContactID gt 12095)&orderby=LastModifiedDateTime ASC,ContactID ASC&expand=Contact&skip=0&top=100
After each page, update the cursor with the LastModifiedDateTime and ContactID from the last record returned. Continue requesting pages until an empty result set is returned, indicating all records have been retrieved.
Changed records since a point in time
The same cursor-based pattern can be used to find records that have changed since a known point in time. This is useful for a scheduled full resynchronisation that runs alongside webhooks — for example, a nightly job that verifies the external system has captured all changes from the past 24 hours.
To find all contacts modified after a specific date:
/api/2012-02-01/auth/resources/contacts/?filter=LastModifiedDateTime gt datetime('2024-01-15T00:00:00.000Z')&orderby=LastModifiedDateTime ASC,ContactID ASC&expand=Contact&skip=0&top=100
Follow the same pagination approach described above to iterate through the results. Note that for ongoing change detection, webhooks are more efficient than polling collection endpoints with a time-based filter.
Pushing changes into Arlo
The sections above describe how to pull data from Arlo into an external system. Many integrations also need to push changes in the opposite direction — writing data from an external system (such as a CRM, HR system, or ERP) back into Arlo.
This is appropriate when the external system is the source of truth for certain information. Common scenarios include:
- HR / people systems — the HR system owns employee records and pushes contact details, job titles, and organisation links into Arlo so that registrant profiles stay current.
- CRM systems — the CRM owns organisation and contact data and pushes company names, addresses, key contact associations, and integration codes into Arlo.
- Identity providers — a directory service pushes contact creation and status changes (e.g. marking a leaver as
Inactive) into Arlo. - External registration / enrolment systems — an external booking platform creates registrations and contacts in Arlo when enrolments are confirmed.
- Data enrichment services — a third-party service populates custom fields, tags, or postal addresses based on external data sources.
The API methods you use depend on whether the record already exists in Arlo:
| Scenario | Method | Description |
|---|---|---|
| Record exists — update specific fields | HTTP PATCH | Partial update of an existing resource. Only the targeted properties are modified; all other data is preserved. |
| Record exists — full replacement | HTTP PUT | Complete replacement of an existing resource. Requires submitting the entire resource representation. |
| Record does not exist | HTTP POST | Create a new resource in the collection. |
In most push scenarios, HTTP PATCH is preferred over PUT because it allows you to update only the fields your system owns, without needing to know or preserve the full resource structure.
Determining source of truth
Before building a push integration, clearly define which system owns which fields. A single resource in Arlo (such as a Contact) may have some properties owned by the external system and others owned by Arlo users. For example:
| Property | Typical owner | Rationale |
|---|---|---|
| Name, email, phone numbers | HR / CRM | Core identity data maintained in the master people system |
| Employment (job title, organisation link) | HR / CRM | Organisational structure managed centrally |
| Postal address | HR / CRM | Office or mailing address from the directory |
| Organisation name, legal name, codes | CRM / ERP | Company master data from the business system |
| Custom fields (classification, membership tier) | Varies | Depends on which system defines the classification scheme |
| Registrations, training history | Arlo | Training data originates in Arlo and should be pulled, not pushed |
Rule of thumb: Your push integration should only write to properties that the external system owns. If both systems can modify the same field, you need a conflict resolution strategy (such as "last write wins" or "external system always wins for this field"). Fields that Arlo owns — such as registrations, event structures, and system-generated timestamps — should only be read by the external system, never overwritten.
Updating existing records with HTTP PATCH
When the external system detects that a record it owns has changed, it should push only the changed fields to Arlo using HTTP PATCH. The recommended workflow is:
- Look up the Arlo resource — use the cross-reference stored in IntegrationData (or a code field such as
CodePrimary) to find the corresponding Arlo record. - GET the current state — retrieve the resource (with appropriate
?expand=parameters if you need to update sub-resources). This is required to construct valid XPath selectors for the PATCH diff document. - Compare fields — diff the external system's values against the current Arlo values. Only include changes for properties that have actually changed and that the external system owns.
- Build and send the PATCH — construct a diff document with the appropriate
<replace>,<add>, or<remove>operations for each changed property. See the HTTP PATCH guide for the diff document format and worked examples.
Example 1 — Push update to contact employment details
An employee has changed roles within their organisation. The HR system pushes the new job title to Arlo by replacing the existing Position value on the Employment sub-resource:
PATCH https://demo.arlo.co/api/2012-02-01/auth/resources/contacts/562/?expand=Employment HTTP/1.1 Accept: application/xml Content-Type: application/xml <diff> <replace sel="Contact/Link[@title='Employment']/ContactEmployment/Position/text()">Senior Developer</replace> </diff>
Example 2 — Push update to organisation name
The CRM has a new legal name and email for an organisation. The existing values are replaced and a new property is added:
PATCH https://demo.arlo.co/api/2012-02-01/auth/resources/organisations/1423/ HTTP/1.1
Accept: application/xml
Content-Type: application/xml
<diff>
<replace sel="Organisation/Name/text()">Acme Corporation Ltd</replace>
<add sel="Organisation">
<LegalName>Acme Corporation Limited</LegalName>
</add>
</diff>
Example 3 — Push update to contact's employment (position & organisation)
An employee has transferred to a different company. The HR system removes the entire Employment link relationship and re-adds it with updated employment details pointing to the new organisation:
NOTE the need for ?expand=Employment on the request URL to ensure the existing link relationship is included in the response and can be targeted by the XPath selector.
PATCH https://demo.arlo.co/api/2012-02-01/auth/resources/contacts/562/?expand=Employment HTTP/1.1
Accept: application/xml
Content-Type: application/xml
<diff>
<remove sel="Contact/Link[@title='Employment']" />
<add sel="Contact">
<Link rel="http://schemas.arlo.co/api/2012/02/auth/related/Employment" type="application/xml" title="Employment">
<ContactEmployment>
<Position>Senior Developer</Position>
<Link rel="http://schemas.arlo.co/api/2012/02/auth/related/Organisation" type="application/xml" title="Organisation" href="https://demo.arlo.co/api/2012-02-01/auth/resources/organisations/1502/"/>
</ContactEmployment>
</Link>
</add>
</diff>
See the HTTP PATCH partial updates guide for the full diff document specification, XPath selector patterns, and additional examples including custom fields and postal addresses.
Creating new records with HTTP POST
When a new record is created in the external system that should also exist in Arlo, use HTTP POST to create the corresponding resource. Common scenarios include:
- A new employee is added in the HR system and needs a contact record in Arlo.
- A new company is created in the CRM and needs an organisation record in Arlo.
- An external enrolment system confirms a booking and needs to create a registration in Arlo.
When creating a record, you can include sub-resources inline with the POST request — such as postal address, employment details (with an organisation link), and custom field values — all in a single request.
Note: IntegrationData cannot be included inline with a POST request. After the resource is created, write IntegrationData as a separate step using the IntegrationData sub-resource endpoint. See Maintaining cross-references below.
Example — HR system creates a new contact with employment details
POST https://demo.arlo.co/api/2012-02-01/auth/resources/contacts/ HTTP/1.1
Accept: application/xml
Content-Type: application/xml
<Contact>
<FirstName>Sarah</FirstName>
<LastName>Chen</LastName>
<Email>sarah.chen@example.org</Email>
<PhoneWork>+64 9 555 0142</PhoneWork>
<CodePrimary>EMP-40221</CodePrimary>
<Link rel="http://schemas.arlo.co/api/2012/02/auth/related/Employment" type="application/xml" title="Employment">
<ContactEmployment>
<Position>Product Manager</Position>
<Link rel="http://schemas.arlo.co/api/2012/02/auth/related/Organisation" type="application/xml" title="Organisation" href="https://demo.arlo.co/api/2012-02-01/auth/resources/organisations/823/"/>
</ContactEmployment>
</Link>
</Contact>
The 201 Created response includes the full resource with the Arlo-assigned ContactID and the IntegrationData sub-resource URI. After creating the contact, write the cross-reference using a separate PUT to the IntegrationData endpoint:
PUT https://demo.arlo.co/api/2012-02-01/auth/resources/contacts/33223/integrationdata/HRSystem/ HTTP/1.1 Accept: application/xml Content-Type: application/xml <ContactIntegrationData> <VendorID>HRSystem</VendorID> <SuppressIntegration>false</SuppressIntegration> <ID>HR-40221</ID> </ContactIntegrationData>
See the Contacts HTTP POST and Organisations HTTP POST documentation for required and optional fields.
Maintaining cross-references with IntegrationData
A push integration needs to match records between the external system and Arlo. The IntegrationData sub-resource is designed for this purpose — it allows you to store external system identifiers against Arlo contacts and organisations, scoped by a vendor ID to avoid conflicts between integrations.
Typical values stored in IntegrationData include:
- The external system's unique identifier for the record (e.g. an HR employee ID or CRM account ID).
- A URI or deep link back to the record in the external system.
- A timestamp of the last successful sync, to support incremental change detection.
- A flag indicating whether the record should be excluded from sync (e.g.
SuppressIntegration).
Recommended workflow:
- On first sync (or POST): Create the resource with HTTP POST, then write IntegrationData as a separate PUT to the IntegrationData sub-resource endpoint (e.g.
/resources/contacts/{ContactID}/integrationdata/{VendorID}/). Store the Arlo-assigned resource ID (e.g.ContactID) in the external system as a reverse cross-reference. - On subsequent syncs: Look up the Arlo resource using the stored cross-reference (either the Arlo ID stored in the external system, or the external ID stored in Arlo's IntegrationData via a filtered collection query on
CodePrimary).
See IntegrationData for the full resource documentation, including how to read and write vendor-scoped data.
Avoiding update loops in bidirectional sync
When your integration both pulls from Arlo (via webhooks) and pushes to Arlo (via PATCH/POST), you must guard against update loops — where a change you push into Arlo triggers a webhook notification, which your handler processes as an external change and pushes back again, ad infinitum.
Strategies to prevent this:
-
Track your own writes: When you push a change to Arlo, record the resource ID and timestamp (or the expected
LastModifiedDateTimefrom the response). When your webhook handler receives an event for the same resource shortly afterwards, compare the modification timestamp and skip processing if it matches a recent write. -
Use IntegrationData timestamps: After each push, write the current UTC timestamp into the resource's IntegrationData (e.g. a
LastSyncedAtfield). In your webhook handler, compare the resource'sLastModifiedDateTimeagainst the stored sync timestamp. If they are very close (within a few seconds), the change likely originated from your integration and can be skipped. - Compare field values before pushing: The compare-then-patch workflow naturally prevents unnecessary writes. If the GET response already contains the values the external system wants to set, skip the PATCH entirely. This avoids both unnecessary API calls and spurious webhook events.
- Respect source-of-truth boundaries: If your webhook handler only processes fields that Arlo owns (e.g. registration data), and your push integration only writes fields the external system owns (e.g. employment details), the two directions operate on non-overlapping properties and loops cannot occur.
