cachedData and callbackUrl: Build One Idempotent SOS State Machine

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

Executive Summary: When a Cobalt Secretary of State search runs long, the first response may now contain cachedData while the live request continues under retryId. A later callback can deliver the completed live result. Treating those payloads as independent results creates duplicate records, stale overwrites, and repeated business actions. The safer design is one lender-owned verification request, one allowed-transition map, and source-aware observations that every poll and callback updates idempotently.

Why Does cachedData Change the Callback Model?

What existed before the interim observation?

A conventional asynchronous flow accepts a request, returns a process identifier, and later delivers or exposes a terminal result. Cobalt documents 202 + Incomplete as continued processing and supports retryId with polling or callbackUrl for completion.[1][2]

The consumer already needed to correlate the later result with its original request. The new feature adds meaningful data before that result, so the request now has an observable interim state rather than an empty wait.

Why can the cached payload not be treated as terminal?

The incomplete response still represents an open live search. Cached data may be stale, may lack a current screenshot, and may differ from the live match. The later live result remains final. Cobalt's status guide distinguishes incomplete processing from completed and error states.[3]

A callback does not start a second verification. It advances the same request that produced the incomplete response.

Which Identity Should Anchor the State Machine?

What does retryId identify?

retryId identifies the Cobalt process used to retrieve a longer-running result. The public SOS API reference includes it as the handle for subsequent status retrieval.[4] Store it exactly and use it for provider correlation.

Do not make it the only identity in the lender's domain. Provider identity alone does not encode the application, business purpose, policy version, or business effect.

What should the lender-owned key identify?

Create or reuse one internal verification-request key for the logical attempt. Attach request inputs, retryId, callback configuration, current process state, observations, and business-action markers beneath it.

• Logical request. One intended SOS verification attempt.

• Provider process. The associated Cobalt retryId.

• Search inputs. State, name, identifier, and material options.

• Delivery path. Polling, callback, or an allowed combination.

• Observation history. Interim cached and final live payloads.

• Effect markers. Unique records for permitted downstream actions.

This separation lets the provider process change state without changing the lender's business identity.

Enforce uniqueness at the logical boundary. For example, the application service can create one active verification request for a specific application, entity, state, and declared attempt number. A resubmission must either reference that request or deliberately create the next attempt. This prevents a network timeout from producing two parallel jobs that later send competing callbacks. The exact key belongs to the lender because only its systems know when two submissions represent the same intended business action.

What Are the Allowed States and Transitions?

Which states are sufficient for a first implementation?

A small, explicit model is easier to test than scattered booleans:

accepted
  -> live_pending_no_cache
  -> live_pending_with_cache
       -> interim_unused | interim_used | interim_review
  -> live_complete_match | live_complete_no_match
  -> terminal_error

The interim-use substate records what the lender did; it does not replace the provider state. Microsoft describes asynchronous request-reply as a pending operation that later reaches a result through a status resource.[5] The same principle applies when useful data appears during the wait.

Which transitions should be rejected?

Reject any event that moves a terminal request back to pending, changes one provider process into another logical request, or converts cache absence into final no-match. Reject an incomplete payload that attempts a final-only action. Quarantine a final response whose identity cannot be resolved.

Define terminal precedence. Once a verified terminal event is committed, a duplicate or late interim event may be stored for diagnostics if policy permits, but it cannot reopen the decision path.

Publish the transition table beside the API consumer and enforce it in code. Each row should name current state, accepted event, validation conditions, next state, and allowed effects. Avoid logic that infers a transition only from whether business fields are populated. A payload with a company name can still be interim, while a terminal no-match can have no company record at all. Status and correlation control the transition.

How Should the First Incomplete Response Be Processed?

What should happen when cachedData is present?

Persist the raw response, source type, updatedAt, receipt time, and request identity in one transaction. Evaluate lender-owned age and match rules, then record a narrow interim permission or review outcome. Keep the top-level state live pending.

Cobalt explains that cached records include timestamps for customer recency decisions[6] and that update timing varies by state.[7] Presence does not imply a universal freshness pass.

What should happen when cachedData is absent?

Persist the incomplete state without an interim observation and wait for live. Cobalt's cache-miss guidance says cache absence does not prove that a business is absent from the state registry.[8]

Use an explicit code such as live_pending_no_cache. Do not create an empty business row or send a no-match message. The same callback or polling path remains responsible for completion.

Set a customer-owned waiting policy for requests that remain pending longer than expected. That policy may alert operations, continue polling with backoff, or initiate an authorized exception route. It should not convert time elapsed into a live no-match. Cobalt publishes state-level response-time guidance to help integrations plan for variable processing windows.[9] Observed duration and terminal business meaning remain different facts.

How Should the Callback Be Accepted Safely?

Which checks should occur before a state transition?

Resolve the intended endpoint, authenticate or verify the request using the consumer's supported control, validate the payload shape, locate the lender request, confirm provider correlation, and acquire an atomic transition guard. Endpoint security is the consumer's responsibility; the new feature does not define a separate signature scheme.

The Standard Webhooks specification describes common patterns such as signing content and using event identifiers, but it is not evidence that Cobalt implements every mechanism in that specification.[10] Follow Cobalt's actual integration contract and your own security review.

How should the handler commit the event?

Store the event and state change atomically where possible. A practical order is: reserve a unique event or transition key, validate the current state, store the raw final observation, compare it with interim data, write the next state, enqueue exactly one permitted business effect, and acknowledge only after the durable commit.

If the handler fails before commit, a repeat can retry. If it fails after commit but before acknowledgment, the unique key turns the repeat into a no-op or a safe readback rather than a second effect.

Keep acknowledgment behavior boring and deterministic. Validate enough to reject an untrusted or malformed request, but do not perform slow underwriting work inside the callback transaction. Commit the verified event, enqueue the one allowed internal transition, and return the documented success response. Long processing before acknowledgment increases timeouts and repeats, while acknowledging before durable storage risks losing the final event.

How Should Polling and Callbacks Coexist?

Can both delivery paths target one request?

Yes, if both use the same transition function. A polling worker and a callback handler may race to deliver the same terminal result. They should contend on one terminal-transition key and converge on the same state.

Cobalt recommends callbacks for long-running or batch workflows while supporting retry-based retrieval.[2] The choice of transport should not change business semantics.

What if a poll completes before the callback arrives?

Commit the terminal result once. When the callback arrives, verify that its identity and content correspond to the completed request, retain only the diagnostics allowed by policy, and do not repeat downstream effects. The reverse order behaves the same way.

AWS recommends idempotency and controlled retry behavior because a repeated request should have the same state effect as one request.[11] Apply that rule to the state transition, not merely to network calls.

How Should Interim and Final Observations Be Preserved?

Why should the final response not overwrite cachedData?

If the interim record was displayed, evaluated, or used, overwriting it removes evidence of what the workflow knew earlier. Store observations as separate immutable versions with source, receipt time, provider timestamp, and request identity.

Cobalt's source guidance distinguishes live and cached provenance.[12] That distinction remains useful after the request closes.

How should a variance enter review?

Compare stable identifiers, names, status, registration state, query inputs, and returned match context. Create a neutral review condition when a material field differs. Do not infer that a difference proves error or misconduct because cached and state searches may rank candidates differently.

Record the review disposition under the same verification request. The automated audit-trail guide provides a broader trace pattern; this state machine supplies the precise interim-to-final event history.

How Should the State Machine Be Tested and Operated?

Which transition tests are essential?

Test cached and no-cache incomplete responses, repeated incomplete events, callback completion, polling completion, callback-poll races, out-of-order interim events, live no-match, identity mismatch, invalid payload, and handler failure before and after commit. Assert both state and business-effect count.

Also assert invariants: one lender request retains one provider process, terminal state never returns to pending, cached source never becomes live source, and one terminal event produces at most one final business effect.

Run concurrency tests, not only ordered fixtures. Deliver the same final payload to two workers at once, delay the first transaction after it reserves the transition key, and let the second worker attempt completion. Repeat with a poll and callback carrying equivalent results. Then send a stale incomplete event after completion. The database state, emitted messages, and visible operations record should all show one terminal transition and no backward movement. Include the resulting trace in the release evidence for later regression review.

Which operational records are needed?

Log the lender request key, retryId, event identity, delivery path, prior and next state, observation source, handler outcome, rejection reason, and resulting action. OWASP recommends logs that capture event status, reason, and action while protecting sensitive data.[13]

Alert on unresolved callbacks, repeated identity failures, illegal transitions, and requests pending beyond a customer-defined threshold. Do not publish performance claims until measured. For related implementation context, read Cobalt's asynchronous webhook architecture guide. To review the transition map against your stack, schedule a demo.

During rollout, compare callback and polling outcomes for the same test matrix and inspect every rejected transition. A rejection may expose an expected duplicate, but it can also reveal a wrong correlation key or an event-order assumption. Release only when both delivery paths preserve the same final result, the same observation history, and the same count of business effects.