Prove that the refund reached the succeeded state. A Refund object does not prove that money moved. This contract proves the succeeded state on the refund resource and a refund.updated event that carries it.

Contract identity.
FactValue
Contract idstripe.refund.succeeded
Version1.0.0
Hash83491ff981adfd05
Completion levelsucceeded
CertificationCommunity
SkillStripe 0.1.0
Valid for provider API versions2026-08-26

What is the intent?

Refund the charge to the customer.

What is the subject and the action?

MemberValue
Subject typestripe.refund
Subject identityrefund_id = $action.result.id
Canonical effectmoney.refund
Provider operationPOST /v1/refunds
Idempotencyidempotency_key through Idempotency-Key, retry is safe

How does the evidence correlate with this operation?

StrategyAssuranceKeysRequired
resource_idstrongrefund_id from $action.result.idyes
idempotency_keystrongidempotency_key from $operation.idno
fingerprintweakcharge from $input.charge; amount from $input.amountno

Which evidence does the contract require?

Minimum evidence level E2. An independent channel is required. Minimum channels: 1.

ChannelLevelIndependenceVerifierDescription
refund_readbackE2provider readbackhttpRead the refund from the provider. The http verifier returns the parsed Refund object, so a condition path is $observed.refund_readback.status.
refund_succeeded_eventsE3provider eventwebhookThe refund.updated events of this refund that carry the succeeded state. The webhook verifier returns {events, count, latest, earliest, types, duplicates_dropped}. A condition reads $observed.refund_succeeded_events.count.
refund_created_eventsE3provider eventwebhookThe refund.created events for the charge since the operation started. The webhook verifier returns {events, count, latest, earliest, types, duplicates_dropped}. A count above one shows a second refund.

Which conditions must all hold for VERIFIED?

ConditionMeaningPathOperatorExpectedEvidence
refund_status_succeededThe refund resource shows the succeeded state.$observed.refund_readback.statuseqsucceededrefund_readback
refund_amount_matchesThe refund amount equals the requested amount.$observed.refund_readback.amounteq$input.amountrefund_readback
refund_created_in_windowThe provider created the refund after the operation started. Stripe writes created in seconds since the epoch.$observed.refund_readback.createdtime_after$operation.created_atrefund_readback
refund_event_succeededAt least one refund.updated event of this refund carries the succeeded state.$observed.refund_succeeded_events.countgte1refund_succeeded_events

Which conditions give CONTRADICTED?

ConditionClassReasonPathOperatorExpected
refund_on_wrong_chargewrong subjectThe refund belongs to a different charge.$observed.refund_readback.chargene$input.charge
refund_amount_mismatchwrong amountThe provider refunded a different amount.$observed.refund_readback.amountne$input.amount
refund_predates_operationpre existing stateThe refund is older than the operation. It proves nothing.$observed.refund_readback.createdtime_before$operation.created_at
duplicate_refund_presentduplicate side effectStripe created more than one refund for the charge since the operation started. Do not retry.$observed.refund_created_events.countgt1

Which observed states map to a verdict before completion?

RuleMatchVerdictReason
terminal_failure$observed.refund_readback.status in ["failed"]FAILEDThe provider reports a failed refund.
still_transitional$observed.refund_readback.status in ["pending","requires_action"]PENDINGThe refund is still moving. The runtime observes again later.
canceled$observed.refund_readback.status eq "canceled"CONTRADICTEDThe refund was canceled. No funds will move.

How long does the runtime observe?

Timing memberValue
Initial delay1000 ms
Poll interval5000 ms
Backoffexponential factor 2, max 60000 ms
Maximum attempts40
Timeout604800000 ms
Stale read window20000 ms
On timeoutUNVERIFIABLE (evidence_unavailable_before_timeout), escalated to a person

Where do these rules come from?

  • stripe.openapi#/paths/~1v1~1refunds/post: stripe.openapi, retrieved 2026-09-05
  • stripe.openapi#/components/schemas/refund/properties/status: stripe.openapi, retrieved 2026-09-05
  • stripe.docs.refunds#refunds/p1: stripe.docs.refunds, retrieved 2026-09-05
  • stripe.docs.refunds#refunds/refund-status/p3: stripe.docs.refunds, retrieved 2026-09-05
  • stripe.openapi#/components/schemas/refund/properties/amount: stripe.openapi, retrieved 2026-09-05
  • stripe.openapi#/components/schemas/refund/properties/created: stripe.openapi, retrieved 2026-09-05
  • stripe.events#/events/refund.updated: stripe.events, retrieved 2026-09-05
  • stripe.openapi#/components/schemas/notification_event_data/properties/object: stripe.openapi, retrieved 2026-09-05
  • stripe.docs.refunds#refunds/events/p2: stripe.docs.refunds, retrieved 2026-09-05

Can stripe.refund.succeeded return VERIFIED from the action response alone?

No. The minimum evidence level is E2. The action response is E1. The completion conditions read refund_readback and refund_succeeded_events.

What happens after the timeout?

The verdict is UNVERIFIABLE with the reason evidence_unavailable_before_timeout. The operation goes to a person for review.