How we shipped a production API out of one Slack thread
The WorkOS IT Contact API went live on August 18 out of a single Slack thread. Humans made the decisions and gated the merge; agents did everything else.
The IT Contact API went live on August 18. It shipped almost entirely out of one Slack thread.

The product call was made in that thread. The design questions were settled in the same place instead of being deferred into a document. Agents handled the work downstream of those decisions: the tickets, the pull requests, the responses to review comments, and the merges.
Agents writing code stopped being remarkable a while ago. What's worth copying here is the conversation around them: nearly every decision this API needed, from the product call to the merge, happened in one thread, in front of everyone who had a stake in it.
The thread was the spec
There was no design doc. The thread opened with a customer request and a two-sentence product call: build an API for managing the customer's IT contact, and don't build one that hands out the long-lived setup link itself, because that's too much security hazard.
The ticket came next, and nobody typed it. Someone asked an agent to capture the decision, and the ticket existed seconds later.
Then the contract got designed the way contracts rarely do, out loud, with the people who had opinions present. The first shape on the table was an RPC endpoint under the API's existing /portal prefix, and in-thread questions pulled it toward REST. The questions that did the pulling were the ones that save late pain: is creating a contact really the same thing as sending a link, and what stops a brand-new email-sending endpoint from being abused?
The thread also caught a wrong assumption before it could ship. The team disagreed about exactly when deleting an IT contact revokes that contact's setup link, tested the behavior mid-conversation, and corrected the assumption. The shipped delete semantics come from that exchange.
About two hours after the brainstorm started, the thread settled on contact CRUD plus invite and revoke. That collected ship reactions and one instruction to an agent: write the plan to the ticket and start.
The thread stayed the spec after implementation began. Should a duplicate create return an idempotent 201 or a 409? A sweep of what other WorkOS APIs return when you re-create an existing sub-resource settled it at 409. Should creating a contact enqueue the same certificate-expiry notification the dashboard enqueues? Yes, because silently diverging from the dashboard was the worse failure mode. Should revoke be able to clear an invitation the caller didn't create? Yes, or a caller who didn't create it could never get unstuck. Each question surfaced with options attached, got a ruling in minutes, and got implemented before it scrolled out of view.
You can see those decisions in the API's final shape. You create, list, and delete contacts, send them an Admin Portal setup invitation scoped to specific features, and revoke it. The scoping is real: the invite call takes an intents array accepting sso, directory_sync, log_streams, domain_verification, and bring_your_own_key. Somebody decided each of those out loud. An agent left alone would have picked a default.
Three agents, one writer
Three agents worked inside the thread, and their lanes were assigned the same way the API contract was: by someone saying so in the thread.
One kept the tracker honest. It turned the product call into a ticket on request, and when the two coding agents later filed duplicate follow-up tickets, it was asked to judge which one survived — then asked for explicit human approval before deleting anything.
The other two split writing from checking. One served as design partner and verifier: proposing and discarding contract shapes with the team, and once code existed, verifying claims against the branch rather than writing more of it. The other carried the implementation across the line: the pull requests, the split when review asked for it, the rebases, and the review responses. The rule that kept them from colliding was itself posted mid-build: one agent writes, the other reviews.
Reviews were debated, not obeyed
Both pull requests went through automated review plus a human pass, and review comments got treated as claims to verify rather than orders to follow.
Some findings were wrong, and the rebuttals carried evidence; the reviewer withdrew them. Others were right and got fixed the same day, including a real security gap and two concurrency bugs. Neither outcome required a human to write the reply.
Human review changed the shape of the thing twice. An automated reviewer struggled with the original single pull request, and the fix was structural rather than an argument about the score: split the work into two stacked PRs, CRUD first, invitations on top. Then a reviewer went deep on the data model the day of the merge, asking whether an organization-scoped API should expose the org-scoped membership or the global record sitting behind it. That reshaped the resource, and the rework landed about thirty minutes after he asked.
Where the human hours went
Deciding, arguing, and reviewing. Almost none of it went into the diff.
One person decided the feature should exist and drew its security line. The team argued out the scope questions in the thread instead of deferring them into a document. One reviewer went deep on the data model right before merge and changed what shipped. Even the merge was human only as a decision: a reviewer approved it and the thread said go, then an agent pushed it into the merge queue that landed it. The tickets, pull requests, and review responses around it were agent work.
Look at what disappeared. Nobody wrote a ticket. Nobody wrote a PR description. Nobody hand-wrote the eight client-library examples that ship with the reference, which covers cURL, .NET, Go, Java, PHP, Python, Ruby, and Rust. Nobody spent an afternoon replying to review comments about naming.
What a thread-to-production pipeline actually needs
Three things, in roughly the order teams get them wrong.
First, a decision-maker present in the thread, answering design questions as they come up. For most teams the blocker sits upstream of agent capability: nobody with the authority to rule on scope is in the room when the question arrives, so the work stalls waiting for a meeting. Here the revoke semantics were raised, argued, and settled in minutes, because the people who could settle them were reading along.
Second, agents that can drive a PR end to end. Opening the PR is the easy half. What decides whether a human gets pulled back in is everything after review: reading the comments, pushing the fix, rebasing when main moves, and pushing back with evidence when the reviewer is wrong.
Third, human review as the only gate, and a real one. Every other checkpoint teams typically insert (spec sign-off, ticket grooming, PR-description review) is a place for work to sit still. Concentrating human attention into one serious pass is also what gave the merge-day review its force: it reshaped the resource model, and the pipeline absorbed the rework in half an hour instead of a sprint.
The obvious objection: this only works on a small, well-understood feature. Largely true. The IT Contact API is five endpoints over an object carrying an email and timestamps. Nobody should read it as evidence that a storage migration ships from a Slack thread. It also breaks down when the design questions cross team boundaries: nobody in this thread had to negotiate a shared data model with another team, which is exactly the situation that turns a thread back into a document. The narrower claim is the useful one. 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 the API does
An IT contact is a member of an organization's IT team responsible for configuring Single Sign-On or Directory Sync, the people on the customer side who do that setup. They also receive critical alerts and error notifications directly from WorkOS.
The useful property is who delivers the link. WorkOS emails the contact the setup link directly, so it never passes through your application, which means you stop relaying portal links through your own app.
Creating a contact sends no invitation on its own, though the contact is notified if the organization has a connection certificate nearing expiry. The invite call is what creates the Admin Portal setup link and emails it.
Two limits to design around. An organization can have at most one active Admin Portal invitation at a time, and invitations can be revoked at any time, so a re-invite flow has to revoke before it sends, via POST /organizations/:organization_id/it_contacts/:contact_id/revoke. And deleting a contact revokes that contact's active setup links along with the record. That's the right default for offboarding, and a trap if you were treating delete as list cleanup.
Both behaviors are worth contrasting with the API-generated Portal link, which expires 5 minutes after creation because WorkOS expects you to redirect the user immediately rather than email them a link. The IT contact invitation covers the case where there is no authenticated session to redirect: the contact is a person at your customer who has never logged into your app.
The setup link opens the Admin Portal, the hosted UI where IT contacts verify domains and configure SSO and Directory Sync connections. Organizations can hold up to 20 IT contacts, and a request that would exceed the limit is rejected without creating any of them.
The Admin Portal is WorkOS's self-serve onboarding surface for enterprise customers: your customer's IT admin walks through a guided, secure setup for SSO or Directory Sync, with no custom admin UI on your roadmap and no support back-and-forth. IT contacts are how you get the right person on their side into that flow.
Full request and response shapes for all five endpoints are in the IT Contact API reference, and the Admin Portal guide covers what the contact lands in.
To copy the process rather than the API: put the person who can settle a design question in the same thread as the people asking it, then remove every gate except the merge.
If you want an agent already sitting in those threads, that's what Atlas is for. It works alongside your team in Slack, out in the open in the channels you already use, so decisions get made where the conversation is happening and turned into action across your tools.