A valid signature still needs a trust decision
An Oprindo sample had a matching asset hash and a valid signature, alongside an untrusted credential result. The useful story is in those separate answers.
An unchanged API schema can still alter a weekly report. Define events, corrections and compatibility from the consumer’s side.
The integration still parses every response. Its completed_at field has the right name and a valid timestamp, and the weekly report runs without complaint. What changed is the event behind that value. The first sign may be a visit disappearing from one week and appearing in the next, long after the schema checks have assured everyone that the response is fine.
Imagine a service platform returning a field called completed_at. A reporting integration uses it to decide which jobs belong in the weekly completion report. Initially the value means the moment an engineer marked a visit complete. Later, the platform changes it to the moment a supervisor approved the paperwork. The field name, timestamp format and response schema remain unchanged. A perfectly valid request can now put a Friday visit into Monday’s report.
Completion and approval are both useful moments to record. The trouble starts when a consumer expecting one receives the other under the same label. That changed meaning can travel through reports, reminders and spreadsheets without a syntax error to slow it down. The schema diff has very little to say about it, which is unfortunate when the person explaining the weekly totals is relying on that diff to identify what changed.
Google's API compatibility guidance distinguishes source, wire and semantic compatibility. The semantic category covers the dependence at issue here: consumers rely on what a value describes, as well as their ability to decode it. For a modest integration, documenting that meaning and reviewing one affected report may be enough to reveal a change that a schema comparison would miss.
For each timestamp, say which event it records, who supplies it and whether it can be corrected. In the visit example, work_completed_at, approval_recorded_at and source_updated_at would let a consumer choose among three different moments. These are proposed names, not a naming standard. Their usefulness is that the weekly report no longer has to guess which of those events the convenient word completed represents.
Now consider a delayed update. The engineer finishes at 16:30 on Friday but submits the record at 09:00 on Monday. The completion timestamp should not quietly become Monday merely because that is when the integration first observed it. Keep the business event and the arrival time separate. Both can matter: one places the work in a report; the other helps explain why it appeared late.
An offset-aware timestamp identifies an instant more clearly than an unqualified local clock value. RFC 3339 provides a standard representation for such timestamps, but choosing a format does not choose the reporting rule. A weekly report may group instants using a named business timezone. A future appointment defined as “09:00 in London” also has scheduling concerns that are not resolved by storing an arbitrary UTC value once and forgetting the location.
Write an example across the boundary the consumer cares about. “Completed Friday at 16:30 local time, recorded Monday at 09:00” is more revealing than a random timestamp on a quiet Tuesday. Then ask which weekly report should include it. The answer turns an attractive field name into a decision that can be checked.
Suppose the engineer later corrects the completion time from Friday to Thursday. Does the API mutate the existing record, create a new revision or append a correction event? Each design can work, but the consumer needs to know how a previously generated report should respond. Polling records that arrived this week will not necessarily find a correction to an older job.
One proposed contract could retain a stable job identity, increment a revision on relevant changes and provide an updated_at field for discovering modifications. A report run records its input cutoff and the revisions it used. A later correction produces an explicit adjustment or a restated report, according to the business rule. This is an application design, not a guarantee that any arbitrary API offers those features.
Restating the earlier week asks what we now believe happened then. Recording an adjustment this week asks when our understanding changed. Both can be legitimate business reports, but they need to be recognisable as different answers. Pick the policy explicitly and retain the inputs it requires. Assigning the correction to whichever report is easiest to update would let an implementation detail decide what the commercial record means.
This is also why a revision is different from a completion timestamp. Changing the timestamp to make modified records easy to find destroys the event’s meaning. Keep discovery metadata separate from the value being corrected. An integration should not have to choose between locating an update and understanding what the update says.
Illustrative events for a weekly reporting contract. Their ordering is meaningful even when every value uses the same timestamp format.
Scroll sideways to see all columns.
| Event | Illustrative time | Consumer question |
|---|---|---|
| Work completed | Friday 16:30 | Which work week owns the visit? |
| Record received | Monday 09:00 | Why did it arrive late? |
| Supervisor approval | Monday 11:00 | When was approval recorded? |
| Time corrected | Tuesday 14:00 | Restate history or show adjustment? |
Take the old weekly report and feed it the proposed response. Which visits move, disappear or look like new work? That is a compatibility review a reporting colleague can join. A general statement that the producer only added or renamed something gives them little help with the changed rows. Keep the old result alongside the new one while deciding which behaviour consumers can continue to rely on.
For the visit example, keep completed_at with its documented old meaning while introducing approval_recorded_at as a separate field, if that is compatible with the existing service. A future major contract can make a more extensive change with a migration plan. Reusing completed_at for approval avoids a new field today by sending the migration work to every consumer tomorrow.
Even adding a response value can require thought. A new status such as completed_pending_review may be a reasonable extension, yet a consumer that handles only open and completed may fail or misclassify it. Define the treatment of unknown statuses and test an old client against the extended response. “We only added something” describes the producer’s edit, not the consumer’s experience.
Arrival order can be misleading here too. A revision received later can describe earlier work, and replaying an old event must not restore an obsolete completion time. Follow the source’s documented revision rule when choosing what supersedes what. If two systems’ revisions cannot be compared, leave that conflict visible. Sorting unrelated numbers may produce an impressively orderly history with no reliable account of which fact replaced another.
Absent, null and empty values also need distinct definitions when the consumer uses them differently. An unrecorded completion time cannot safely become midnight, and an unavailable value does not establish that no visit took place. Preserve that uncertainty in the fallback and expected report output. Otherwise a structurally valid default can change membership just as readily as redefining the completed_at field itself.
A correction from Friday to Thursday requires an explicit reporting policy, beyond storing separate timestamps.
Scroll sideways to see all columns.
| Policy | Question the report answers | Required record |
|---|---|---|
| Restate historical report | What do we now believe happened that week? | Revised event and report inputs |
| Current-period adjustment | What changed in our understanding this week? | Correction event and affected earlier record |
For a modest integration, I would keep a short, versioned pack containing the field definitions, representative payloads and expected report membership. Include an on-time visit, a late-arriving completion, a supervisor approval in the next week and a correction to an earlier revision. These cases exercise the promise across time rather than merely proving that the JSON is well formed.
Include the expected output as well as the input. The pack should state its scope. It might cover the reporting consumer without proving every mobile client or export remains compatible. That is still useful evidence. If a producer change affects another consumer, add the relevant behaviour rather than pretending the original four examples were a complete specification of the business.
There is a cost to keeping two fields or maintaining an older meaning. Documentation can become repetitive, and consumers may need guidance about which field to choose for new work. That cost should be visible. So should the alternative: an apparently elegant cleanup that changes historical totals and leaves somebody explaining a report whose rows no longer mean what its heading says.
Use the Friday completion, Monday approval and later correction as the shared example when reviewing the contract pack. Both teams can see which report should contain the visit and what a correction should do to an already-issued result. If the new response changes that outcome, discuss the migration while the old meaning is still available. The field definition then records a decision about real rows, rather than a tidy name that every consumer has to interpret again.
Working on a workflow like this? Hamsa helps finance and operations teams connect their tools and handle the exceptions, starting with one workflow.
See the Payment Operations PilotPrimary sources behind this article. Living documentation may change.

Practical engineering. A human perspective.
More about Hamsa Software and the person behind it. Editorial notes.