# What does stripe.refund.succeeded prove?

> Prove that the refund reached the succeeded state. Version 1.0.0, certification Community. Every condition and timing rule of the signed Stripe skill.

Canonical: https://provely.sh/verify/stripe/stripe.refund.succeeded  
Last reviewed: 2026-09-05  
Skill version: 0.1.0  
Certification: community  

**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.*

| Fact | Value |
| --- | --- |
| Contract id | `stripe.refund.succeeded` |
| Version | `1.0.0` |
| Hash | `83491ff981adfd05` |
| Completion level | `succeeded` |
| Certification | Community |
| Skill | [Stripe 0.1.0](/verify/stripe) |
| Valid for provider API versions | `2026-08-26` |

## What is the intent?

Refund the charge to the customer.

## What is the subject and the action?

| Member | Value |
| --- | --- |
| Subject type | `stripe.refund` |
| Subject identity | refund_id = `$action.result.id` |
| Canonical effect | `money.refund` |
| Provider operation | `POST /v1/refunds` |
| Idempotency | idempotency_key through `Idempotency-Key`, retry is safe |

## How does the evidence correlate with this operation?

| Strategy | Assurance | Keys | Required |
| --- | --- | --- | --- |
| `resource_id` | strong | refund_id from `$action.result.id` | yes |
| `idempotency_key` | strong | idempotency_key from `$operation.id` | no |
| `fingerprint` | weak | charge from `$input.charge`; amount from `$input.amount` | no |

## Which evidence does the contract require?

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

| Channel | Level | Independence | Verifier | Description |
| --- | --- | --- | --- | --- |
| `refund_readback` | E2 | provider readback | `http` | Read the refund from the provider. The http verifier returns the parsed Refund object, so a condition path is $observed.refund_readback.status. |
| `refund_succeeded_events` | E3 | provider event | `webhook` | The 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_events` | E3 | provider event | `webhook` | The 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?

| Condition | Meaning | Path | Operator | Expected | Evidence |
| --- | --- | --- | --- | --- | --- |
| `refund_status_succeeded` | The refund resource shows the succeeded state. | `$observed.refund_readback.status` | `eq` | `succeeded` | `refund_readback` |
| `refund_amount_matches` | The refund amount equals the requested amount. | `$observed.refund_readback.amount` | `eq` | `$input.amount` | `refund_readback` |
| `refund_created_in_window` | The provider created the refund after the operation started. Stripe writes created in seconds since the epoch. | `$observed.refund_readback.created` | `time_after` | `$operation.created_at` | `refund_readback` |
| `refund_event_succeeded` | At least one refund.updated event of this refund carries the succeeded state. | `$observed.refund_succeeded_events.count` | `gte` | `1` | `refund_succeeded_events` |

## Which conditions give CONTRADICTED?

| Condition | Class | Reason | Path | Operator | Expected |
| --- | --- | --- | --- | --- | --- |
| `refund_on_wrong_charge` | wrong subject | The refund belongs to a different charge. | `$observed.refund_readback.charge` | `ne` | `$input.charge` |
| `refund_amount_mismatch` | wrong amount | The provider refunded a different amount. | `$observed.refund_readback.amount` | `ne` | `$input.amount` |
| `refund_predates_operation` | pre existing state | The refund is older than the operation. It proves nothing. | `$observed.refund_readback.created` | `time_before` | `$operation.created_at` |
| `duplicate_refund_present` | duplicate side effect | Stripe created more than one refund for the charge since the operation started. Do not retry. | `$observed.refund_created_events.count` | `gt` | `1` |

## Which observed states map to a verdict before completion?

| Rule | Match | Verdict | Reason |
| --- | --- | --- | --- |
| `terminal_failure` | `$observed.refund_readback.status` in `["failed"]` | FAILED | The provider reports a failed refund. |
| `still_transitional` | `$observed.refund_readback.status` in `["pending","requires_action"]` | PENDING | The refund is still moving. The runtime observes again later. |
| `canceled` | `$observed.refund_readback.status` eq `"canceled"` | CONTRADICTED | The refund was canceled. No funds will move. |

## How long does the runtime observe?

| Timing member | Value |
| --- | --- |
| Initial delay | 1000 ms |
| Poll interval | 5000 ms |
| Backoff | exponential factor 2, max 60000 ms |
| Maximum attempts | 40 |
| Timeout | 604800000 ms |
| Stale read window | 20000 ms |
| On timeout | UNVERIFIABLE (`evidence_unavailable_before_timeout`), escalated to a person |

## Where do these rules come from?

- `stripe.openapi#/paths/~1v1~1refunds/post`: [stripe.openapi](https://raw.githubusercontent.com/stripe/openapi/master/openapi/spec3.json), retrieved 2026-09-05
- `stripe.openapi#/components/schemas/refund/properties/status`: [stripe.openapi](https://raw.githubusercontent.com/stripe/openapi/master/openapi/spec3.json), retrieved 2026-09-05
- `stripe.docs.refunds#refunds/p1`: [stripe.docs.refunds](https://docs.stripe.com/refunds), retrieved 2026-09-05
- `stripe.docs.refunds#refunds/refund-status/p3`: [stripe.docs.refunds](https://docs.stripe.com/refunds), retrieved 2026-09-05
- `stripe.openapi#/components/schemas/refund/properties/amount`: [stripe.openapi](https://raw.githubusercontent.com/stripe/openapi/master/openapi/spec3.json), retrieved 2026-09-05
- `stripe.openapi#/components/schemas/refund/properties/created`: [stripe.openapi](https://raw.githubusercontent.com/stripe/openapi/master/openapi/spec3.json), retrieved 2026-09-05
- `stripe.events#/events/refund.updated`: [stripe.events](https://docs.stripe.com/api/events/types), retrieved 2026-09-05
- `stripe.openapi#/components/schemas/notification_event_data/properties/object`: [stripe.openapi](https://raw.githubusercontent.com/stripe/openapi/master/openapi/spec3.json), retrieved 2026-09-05
- `stripe.docs.refunds#refunds/events/p2`: [stripe.docs.refunds](https://docs.stripe.com/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.

## Read next

- [Read the Stripe verification page](https://provely.sh/verify/stripe)
- [Read the stripe.refund.created contract](https://provely.sh/verify/stripe/stripe.refund.created)
- [Read what a completion contract contains](https://provely.sh/contracts)
- [Read what a receipt proves](https://provely.sh/receipts)
