Skip to main content
Wildo.ai Coming soon

Systems of record

Refresh a record as soon as the ERP reports a change

When the ERP announces that particular records changed, the application re-reads just those records from the ERP right away instead of waiting for the next scheduled sync. The announcement only says which records to re-read; their contents always come from the ERP.

An Odoo rename sends an Id notification so the application re-reads one account, while the separate hourly pass continues.

Refresh a record as soon as the ERP reports a change

Many ERPs can announce a change as it happens: an Odoo automated action, a Dataverse webhook. The announcement reaches the application’s own webhook endpoint, and the code that handles it may name the records that changed. The copy of those records is then re-read from the ERP right away, through the pipeline itself, instead of on the next scheduled pass.

The announcement is treated as a hint, never as data. Nothing it carried is written to a record: the pipeline asks the ERP for the named records and loads the ERP’s answer through its usual mapping, checks and erasure blocks. A forged or replayed announcement can at worst cause a re-read of a record the ERP would serve anyway.

Nothing asked for is lost. The named records are recorded before the re-read is requested, and asking twice for the same record is one request. If a scheduled pass is running at that moment, the request waits and the next run of any kind serves it first. A record the ERP no longer serves is reported, not deleted, since deletion is a full pass’s decision. A re-read that fails never stops the pass that follows it.

Example — A client renamed in the ERP

Someone renames a client account in Odoo, and an automated action posts the account’s id to the application. The application re-reads that one account and the new name appears in the copy, while the hourly pipeline carries on as before.

For engineers

An inbound handler declares the pipelines it may ask in pipelineKeyRequests and calls ctx.pipelines.requestPipelineKeys. No application declares such a handler yet; the following is the example the engine documents beside the API, in inbound-handler-pipelines.backend.definitions.ts, with the handler’s source, payload schema and roles elided as in the original.

defineInboundHandler({
  ref: 'odoo.partner-changed',
  // …source, payloadSchema, scopeMode: InboundHandlerScopeMode.APPLICATION, actingRoles…
  pipelineKeyRequests: [AppResourceType.ERP_CUSTOMERS],
  handle: async (ctx, payload) => {
    const { malformedKeys } = await ctx.pipelines.requestPipelineKeys(AppResourceType.ERP_CUSTOMERS, payload.ids.map(String));
    // malformedKeys: ids the pipeline's key codec cannot read — log them; the rest are recorded.
  },
});

Keys are the remote keys the pipeline stores in its remoteKeyLocalField. With one key field, the id as the sender spells it is accepted and normalized. A key that does not decode is returned in malformedKeys, never recorded and never thrown about, so one bad id does not make the delivery retry forever. A call for a resource the handler did not declare throws. One call may name at most 1,000 keys; a notification naming more is a bulk change, which a full pass handles.

The handler’s roles do not apply to the re-read, because the handler writes nothing: the pipeline loads what the ERP serves, exactly as a scheduled pass would.

What the re-read does

The door records each key, then asks for a keyed run. A keyed run re-reads exactly those records — Odoo search_read by ids, OData v4 key predicates — within the pipeline’s sourceFilter, and loads them through the same transform, erasure suppressions, reference resolution and lease as a pass. Every inbound run drains waiting requests before its own pass, so a request made while a pass held the pipeline is served by the next run, and a record loaded this way during a long full pass joins that pass’s membership and is never orphaned by it.

A requested record the ERP does not return is counted as missing and its request dropped; the local record stays. A page whose load fails keeps its requests for the next run. On a two-way pipeline, a re-read record is decided exactly as a pass decides it, so a shared field changed on both sides is a conflict under the declared policy.

The run history and the audit trail name the delivery that asked for the re-read and how many keys it named. An operator can also run the keyed re-read directly with passMode: 'keyed' on the run route.

What startup refuses

A handler naming a resource with no pipeline, an outbound pipeline (it reads nothing to re-read), or a pipeline whose dialect has no keyed read is refused at startup. Keyed reads exist for Odoo JSON-RPC with a single key field and for OData v4.

Building a B2B product or an internal tool?

Wildo is not self-service yet. Tell us what you have in mind and we will say plainly whether it fits, and what happens next.