Skip to main content

Idempotency

You know when a retry is the same logical operation; we don't. Pass idempotencyKey on any mutation and we'll dedupe within 24 hours — second request with the same key gets the cached first response, not a duplicate row.

How it wires up

Every create / update / sub-resource .create on the SDK accepts an options bag with idempotencyKey. The client adds it to the Idempotency-Key header before sending.

basic.js
await cstar.tickets.create(
  { title: 'Need help', priority: 'high' },
  { idempotencyKey: 'submission_form_42' }
);

// Same key inside 24h returns the cached first response — no duplicate ticket.
await cstar.tickets.create(
  { title: 'Need help', priority: 'high' },
  { idempotencyKey: 'submission_form_42' }
);

Pick the key like a fingerprint

Make the key deterministic for the logical operation, not the network attempt. Some shapes that work:

  • Form submissions: submission_${formId}_${userId} — same form, same user, same ticket.
  • Webhook handlers: stripe_${event.id} — Stripe sends the same event ID on every retry.
  • Queue jobs: job_${jobId} — survives worker restarts and at-least-once delivery.
  • Bad shape: crypto.randomUUID(). A fresh UUID per attempt is the same as no key.

Webhook handler that survives retries

The classic case. Stripe (or any webhook source) retries delivery on non-2xx responses; the idempotency key keeps you safe even when your handler runs twice.

stripe-handler.js
import express from 'express';
import { CStarClient } from '@cstar.help/js';

const cstar = new CStarClient({
  apiKey: process.env.CSTAR_KEY,
  teamId: process.env.CSTAR_TEAM_ID
});

app.post('/stripe-webhook', async (req, res) => {
  const event = req.body;

  await cstar.tickets.create(
    {
      title: `Stripe issue: ${event.type}`,
      priority: 'high',
      tags: ['stripe', event.type]
    },
    { idempotencyKey: `stripe_${event.id}` }
  );

  res.status(200).end();
});

No key, no dedup

Skip idempotencyKey and you get exactly-once semantics on the network and at-least-once on retries. That's fine for fire-and-forget endpoints (analytics events, log writes) but a footgun for anything that creates a row your customer cares about.

Next up