WorkOS Chose 409 Over an Idempotent Create, On Purpose

WorkOS Chose 409 Over an Idempotent Create, On Purpose

WorkOS published an account of shipping a production API out of one Slack thread, and buried inside the process story is a set of API design decisions worth more than the process. The result is the IT Contact API: five endpoints, eight client-library examples, a maximum of twenty IT contacts per organization, one active Admin Portal invitation at a time, and setup links generated through the API that expire in five minutes. The shape changed mid-thread from RPC to REST. Creating a contact deliberately does not send an invitation — that is a separate invite call taking an intents array of sso, directory_sync, log_streams, domain_verification or bring_your_own_key. Deleting a contact revokes their active setup links as a cascade rather than leaving them live.

The decision that deserves attention is the one they made against convention: a duplicate create returns 409, not an idempotent 201 with the existing record. That is the harder choice to defend in a design review and the better one here, because an IT contact is a person an admin believes they are adding for the first time. A silent 201 tells the caller they succeeded at something that did not happen; a 409 makes the caller reconcile their own state. The process claim around it is narrower than the headline suggests, and they state it precisely: “for the large class of work that becomes well-scoped the moment someone decides the scope, deciding is the bottleneck, and deciding is what belongs in the thread.” What disappeared was ticket-writing and PR descriptions, not review — and “review comments got treated as claims to verify rather than orders to follow,” which is a distinction worth stealing whether or not an agent wrote the branch.

The catalog reads 41 API pages for WorkOS, and this new surface sits at the junction of several. The admin-portal API is what those five-minute links open onto; the organizations API is what a contact is scoped to; and the intents array is a list of other people’s endpoints, resolving to the sso API, the directory-users API and the organization-domains API. An invitation that declares which setup flow it is for is a small piece of design that saves an integrator from guessing which of four onboarding paths a customer is on.

WorkOS scores 52.6, developing on the Kin Score, on contract quality at 69.8 and developer ergonomics at 63.1. Agent Readiness is 38.3, agent-ready, and this post makes one of the lit dimensions more interesting than a binary can express: idempotency reads lit, while the design decision documented here is a deliberate refusal to be idempotent on create. Both are true. A provider can be rigorous about retry semantics on the operations where replay is safe and still choose 409 where a duplicate means the caller is confused. What the record does not carry is that reasoning — error_semantics is unlit, so the one thing this post explains best, why this status code and not that one, exists only in the blog. Second time in a week that has been the gap worth naming.

← Pulumi Makes Infrastructure a Graph an Agent Can Query