Skip to main content

Idempotency

A request that times out leaves you unsure whether it ran. Idempotency lets you retry a write and get the original result back instead of a duplicate. The API offers it in two places, and it matters which one you are using.

Intents: idempotency_key in the body​

Human-approved actions are proposed through POST /intents (see Intents). The request body accepts an optional idempotency_key:

curl -X POST "https://api.wefunder.com/intents" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"action_name": "comments.create",
"resource_type": "Company",
"resource_id": "co_9Xk2mP4qR7sT1uV3wY5zA8bC",
"params": { "target_type": "company", "target": "co_9Xk2mP4qR7sT1uV3wY5zA8bC", "body": "What is your current monthly revenue?" },
"idempotency_key": "ask-revenue-co_9Xk2mP4qR7sT1uV3wY5zA8bC-2026-09"
}'
  • Same key, same operation (action, resource, params): the existing intent is returned with 200 as long as it is pending, approved, executing, or executed. No second intent is minted and the call does not count toward the intent rate limit.
  • Same key, different operation: 409 with type: "idempotency_conflict". Nothing is created.
  • If the earlier intent is rejected, expired, or failed, the key is released and a new intent is created.

The key is scoped to your application and the authorizing user, so two apps can use the same string without colliding.

On Wefunder-Version: 2026-10-01 only (both were removed in 2026-10-15; closing a deal happens on wefunder.com), the deal lifecycle endpoints (POST /syndicates/{id}/deals/{fundraise_id}/close and /finalize) are idempotent without a key: a repeated call while an intent for that deal is still pending returns the same pending intent.

Partner endpoints: the Idempotency-Key header​

Preview

The header-based scheme below is honored by the Partner API write endpoints: drafting a syndicate deal and invite links (invite-only preview), and investment sessions (beta). Public endpoints outside the intent system do not read this header today.

Send an Idempotency-Key header on the write. Keys are scoped to your application and user and live for 24 hours.

curl -X POST "https://api.wefunder.com/syndicates/syn_abc123/deals" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Idempotency-Key: acme-series-a-spv-create" \
-H "Content-Type: application/json" \
-d '{ ... }'
SituationResult
First request with the keyRuns normally; the created resource is recorded against the key.
Retry, same path and same parameters200 with the originally created resource. Nothing new is created.
Retry, same key but a different path or parameters422 with type: "idempotency_conflict".
The original request failedNothing is recorded; a retry runs the write again. Failures are never cached.
No headerThe request runs with no deduplication.

Two concurrent requests with the same key are serialized on the server; the loser receives the winner's result.

Choosing keys​

Derive the key from the operation you intend, so a retry produces the same key and a new operation produces a new one.

// Good: one key per intended operation
const key = `spv-create-${deal.id}-v${deal.version}`;
const key = `remove-${memberId}-${today}`;

// Bad: a fresh UUID per attempt defeats the purpose
const key = crypto.randomUUID();

// Bad: the same key for different operations conflicts on the second use
const key = "create-spv";

Keep keys under 255 characters and log them alongside the request so a support conversation can find the original call.

Naturally idempotent methods​

GET, PUT, and DELETE are safe to repeat; a second DELETE on an already-removed resource returns 404 or 409 rather than doing anything. POST creates, and PATCH may not be idempotent when it appends. Use a key on those.

Was this page helpful?