How to Turn cachedData updatedAt Into a Defensible Cache-Age Gate

September 16, 2026
September 16, 2026
10 Minutes Read
Secretary of State APIblog main image

Executive Summary: When a Cobalt live Secretary of State search takes longer than the first request window, an incomplete response may now include cachedData and its updatedAt timestamp while the live search continues. That timestamp answers one question: when was this cached record last updated? It does not decide whether the record is recent enough for an underwriting step. The lender must calculate age, apply a policy tied to the intended action, and wait for the live result whenever the rule is absent or fails.

What Does updatedAt Tell the Integration?

Which time does the field represent?

The new interim payload uses updatedAt for the cached record's update time. It is record-age evidence, not the time the current request started and not the time the lender received the response. Cobalt's public cache guidance likewise tells consumers to use record timestamps to assess recency for their own use case.[1]

Store at least three clocks separately: the provider's updatedAt, the time your system received the interim response, and the time the age rule ran. Collapsing them into one field makes it difficult to reconstruct what the workflow knew.

Which decisions does the timestamp not make?

updatedAt does not declare the record current, approved, verified, or safe for a credit decision. The same age may be acceptable for filling an internal review screen and unacceptable for a final verification gate. Cobalt's state-cache guidance also explains that update frequency varies rather than promising one universal schedule.[2]

A timestamp is evidence for a policy decision. It is not the policy decision.

How Should a Lender Calculate Cache Age?

What is the basic calculation?

Convert updatedAt to a timezone-aware instant, compare it with a trusted evaluation time, and retain the inputs with the result. If the API value is expressed as epoch milliseconds, do not accidentally parse it as seconds.

record_updated_at = parse_epoch_milliseconds(cachedData.updatedAt)
evaluated_at = trusted_utc_clock()
age_seconds = max(0, evaluated_at - record_updated_at)
policy_result = evaluate(action, state, age_seconds, policy_version)

The max(0, ...) guard should not silently approve a future timestamp. A negative raw age should create a clock or data-quality exception. RFC 9110 notes that recipients need date values and local clocks to reason about message age, while also recognizing that clocks may not be perfectly synchronized.[3]

What should happen when parsing fails?

Fail closed to the live-result path. Preserve the raw value, record the parse error, and do not substitute the current time. Treating an invalid value as newly updated converts missing evidence into a favorable age result.

The incomplete request remains open. Cobalt defines 202 + Incomplete as continued processing, so the safe fallback is to wait or use the documented callback path rather than start a contradictory business conclusion.[4]

How Should the Policy Be Written?

Why must the rule name an action?

A useful policy does not say, "cached records under N days are valid." It says which action may occur, under which threshold, with which state context, and what remains blocked. The rule should be narrow enough that an engineer and an operations reviewer reach the same branch.

• Action. Name the reversible task, such as prefill or queue prioritization.

• Threshold. Set the maximum age for that task.

• Jurisdiction context. Record whether the rule changes by state or registry behavior.

• Required match fields. Identify what must agree before any interim use.

• Fallback. Specify wait for live, manual review, or no interim action.

• Policy version. Retain the exact rule set used at evaluation time.

This is lender-owned policy. Cobalt supplies the timestamp and continues the live lookup; it does not set the lender's acceptable age.

Why is one enterprise-wide number often incomplete?

One threshold may be appropriate for one low-risk action. It should not silently become the threshold for every product, state, and decision. Cobalt states that cache behavior can vary by jurisdiction,[2] and the new feature brief warns that interim data may be stale.

Start with action categories rather than a large state table unsupported by evidence. For example, a lender may allow display at one age, permit reversible prefill at a narrower age, and require live completion for final verification at every age.

Write thresholds in machine-readable units and in plain operating language. A configuration entry might use seconds, while the policy page says "less than seven complete days at evaluation." Specify whether the boundary is inclusive, because a record exactly at the threshold should not pass in one service and fail in another. Name the timezone and rounding rule as well. These details look small until a production case lands on the boundary and two systems produce different outcomes.

How Should the Gate Handle the Live-Pending State?

What may happen after an age pass?

An age pass should permit only the action named in the policy. It may let the system populate a clearly labeled review card, compare an entity name, or prepare a checklist. It should not change the top-level verification from live pending to complete.

Attach the permission to the observation and request, not merely to the business entity. A different application, a later request, or a changed search input should trigger a new evaluation. Otherwise, one earlier pass can leak into a context the policy never evaluated. The result should resemble interim_prefill_allowed or display_only, not a general verified flag that downstream services may interpret too broadly.

Cobalt's status guide distinguishes incomplete processing from terminal results.[5] Keep that distinction in your own state model even when the cached record looks complete.

What must happen after an age fail?

Record the failed rule and wait for live. Do not reinterpret an old record as a negative match or a provider failure. The data exists; it simply did not satisfy the lender's permission for this action.

Use the same pending request and its retryId. Cobalt documents retryId as the handle for checking a long-running search until completion.[6] Starting a second request can make correlation and downstream controls harder.

How Should Missing cachedData Differ From Old cachedData?

What does an absent interim record mean?

The feature adds cachedData only when an interim cached match is available. If the field is absent, preserve the live-pending state. Cobalt's cache-miss guidance says an unsuccessful cached lookup does not prove that the business is absent from a state registry.[7]

An absence branch has no record age to calculate. It is not an automatic age failure and should not create a fabricated timestamp.

What does an old interim record mean?

An old record is an observation that failed a named age rule. Retain it with the calculated age and policy result if your retention policy permits. That history can explain why the workflow waited even though data was visible.

Use separate outcome codes such as cache_absent, cache_age_rejected, cache_match_rejected, and live_final_no_match. Cobalt's documented statuses carry different process meanings, and internal codes should preserve rather than erase those meanings.[5]

How Should the Gate Be Logged and Reviewed?

Which fields belong in the decision event?

Log the lender request key, Cobalt retryId, state, raw updatedAt, parsed timestamp, evaluation time, calculated age, intended action, threshold, policy version, outcome, and next state. OWASP recommends recording the event's when, where, who, and what, including status, reason, and action.[8]

Avoid writing sensitive application data into general logs. Use internal references and apply the lender's access, retention, and redaction policy. NIST treats log management as a lifecycle involving generation, storage, access, and disposal, not merely writing one event.[9]

What should a reviewer be able to reconstruct?

A reviewer should be able to answer: Which interim record arrived? How old was it at evaluation? Which rule applied? What action, if any, occurred? When did the final live result arrive? Did it change the disposition?

Keep the calculation reproducible. Store the unrounded age or both source times, not only a label such as fresh. Retain the policy version rather than only the current threshold. If the business later changes a seven-day rule to three days, historical events should still show why an earlier observation passed under the rule in force at that time. This is operational explanation, not a claim that a particular retention period satisfies a legal requirement.

The audit record should not claim that the cache was verified live. It should say that a cached observation passed or failed a customer-defined interim-use rule while live verification remained pending.

How Should the Final Live Result Close the Gate?

Does a passing age rule remain final?

No. The new feature keeps the completed live search as the final result. When that response arrives, store it separately, compare any material fields, and close the open request according to lender policy. Do not let the final payload overwrite the cached observation if anyone or any system used the earlier data.

Cobalt's guidance on identifying cached and live responses emphasizes tracking source and timing.[10] The interim-to-final transition should retain both.

What if the cached and live records differ?

Route material variance to a neutral identity-review path. Different results can reflect different search and ranking behavior; they do not by themselves establish misconduct. Compare stable identifiers, query inputs, registration state, names, and other returned evidence.

The verification waterfall guide explains why source layers need distinct provenance. The age gate covered here answers only whether one interim action was permitted.

What Is the Smallest Safe Rollout?

Which controls should ship first?

Begin with one reversible action, one explicit threshold, a wait-for-live default, immutable observation storage, and metrics that describe branch frequency without claiming a business lift. Test boundary ages, missing values, invalid values, future timestamps, cache absence, and later live variance.

Release the rule behind a customer-controlled feature flag if your architecture supports one. First observe how often the branch appears and how frequently the live result later differs. Then decide whether to widen the set of reversible actions. A staged rollout limits the blast radius of a parsing, timezone, or policy-mapping mistake without weakening the live-completion requirement.

AWS recommends retry backoff and idempotent handling for asynchronous work.[11] Make the age decision idempotent too: repeating the same interim event should not produce a second business effect.

Which release questions should be answered?

Confirm who owns the policy, how the threshold changes, how an underwriter sees source and age, what happens on rule failure, how duplicate events behave, and how final-live variance enters review. Measure current processing rather than promising savings that have not been observed.

For a broader implementation pattern, see Cobalt's retry and graceful-failure guide. If your team wants to map the age gate into an existing workflow, schedule a demo to review the states.