Sprites Connectors is a great example of Terrible API Design
What Sprites Connectors gets wrong, how to avoid the same mistakes, and how to design APIs for agents.
Coming here to hate on another API is uncommon for me. However, having just read the Connectors docs, they seem exceptionally poorly designed. Unlike a lot of poorly designed APIs, Sprites had references to go off of; they didn’t need to suck. There are many other players in the sandboxing space who offer similar APIs and did a non-terrible job.
I’m gonna talk through what I hate about Sprites Connectors design, why it sucks, how not to make their mistakes, the better way, and how to design non-terrible APIs.
The Sprites Connectors API
Sprite Connectors are credential injection at the network level so agents inside of a sandbox never see it. They do things like “inject an authorization header to OpenAI”, or “inject an authorization header to Anthropic”, or “inject an authorization header to Stripe”. But the API doesn’t stop there, it can also “inject an authorization header to S3” and “inject an authorization header to Meta”! (Okay, S3 actually needs SigV4 signing, and Stripe would use a Custom API connector.) You can also create minimal other connectors to HTTP APIs via hostname, path and what header you want to add.
What a complex dynamic API, it can inject authorization headers for all of those providers.
Why it sucks
Starting out, it's human first (and kinda human only). The primary documented way to set up connectors is through the dashboard. They do document a REST API for creating, testing and rotating connections, but it's a second-class citizen in the guide.
A year ago someone could’ve argued “This API isn’t for you, it's designed for luddites who don’t use AI and don’t like reading code”. That argument is no longer valid in today’s agent landscape. I haven’t met an engineer in the past 6 months who isn’t coding with AI. While some may exist in rural Wyoming, I actually know who Fly Sprites is trying to sell to: “Computers for agents”; all of the people building agents who need computers, use agents. With that in mind, a good API design works with the agents, not against them by being dashboard first.
Second, and really the serious reason, it's overly opinionated about providers and token placement in a way that achieves very little for a simple HTTP key. When designing a good API, it's good to think about how it will be used and the purpose for which it's being built; Sprites Connectors clearly did this part well. But once you have a grasp of what it's for you should take a step back and look at the functionality you're exposing.
A job scheduler with POST /hourly-job and POST /daily-job really needs POST /jobs with a schedule. Sprites Connectors did not make this leap for simple HTTP injection.
This is compoundingly bad when you realize how limiting their API surface is. They’ve built out a system that makes “auth_header_prefix” a first-class citizen. A Custom API connector does let you put a token in a named header, but what if I want to inject two headers, or patch a field in the JSON body?
Reading this API, I feel like I’m reading the work of a 2nd year college student who just learned what object oriented programming was and thought it would be good to put all their classes into the API. The amount of first class crap that doesn’t make sense to be first class is overwhelming.
How not to do this
There are a few good strategies to avoid APIs like this.
The first obvious one is to look for past work. When Freestyle was designing our header injection APIs, we looked around. Deno Sandbox substitutes secret placeholders on outbound requests to approved hosts. Vercel has a fairly minimal one that says transform requests to these domains with these headers. I found this to be cool but less powerful than we wanted: we also wanted to support things like Postgres credentials, which aren't HTTP headers. After reviewing other approaches, Vercel’s transformation API was the closest to what we wanted, extremely well designed, adaptable and a good starting point.
We reviewed all of these with a few lenses:
We knew we had some functionality we wanted and we wanted to see what functionality other people had come up with. This helped us flesh out our use cases, find new ones, and learn from what other teams had discovered. Whenever an API could do something we didn’t originally plan on supporting, we discussed whether or not it was worth supporting for our users, and when it was we added it to our requirements.
We wanted to see how adaptable these were, if Deno wanted to add support for E2B style connectors could they, and vice versa. Would it fit into their API or would it be a new version? A good API accepts that changes will come in the future and plans for them.
Built in vs Build On. Can you make this functionality through their primitives or do they have to build it in? A good API can be built on, that way patterns you haven’t accounted for still function. Sprite's Custom API connector handles four token-placement methods, but a different request change needs new first-class support.
This isn’t a silver bullet and there aren’t always good existing APIs. It’s not a good answer to say “Well Vercel did it this way so will we”, but it is a useful data point.
Once you have a good enough set of use cases it's good to consider what the underlying API actually does. A good API generally isn’t coded to the solution it was meant to provide but rather the underlying mechanism used to solve it. For a mental model, an OpenAI API key connector and a PostHog API key connector can both be a rule that matches a domain and injects a header. OAuth token refresh and S3 SigV4 signing are different mechanisms and actually deserve their own integrations.
Once you have a good grasp on this, it's good to build out the API and try using it in a couple things and get some feedback.
Better APIs
The two best APIs I’ve seen for transformations are Vercel and Freestyle (inspired by Vercel).
Vercel Transforms are great because they are clearly built to be extended. You can clearly see that instead of building the “Example.com Host Header Integration” that Sprites would do, Vercel built a generic system that allows them to support this use case without any prebuilt API for it.
import { Sandbox } from '@vercel/sandbox';
const apiKey = process.env.EXAMPLE_API_KEY!;
const sandbox = await Sandbox.create({
networkPolicy: {
allow: {
"api.example.com": [{
transform: [{ headers: { "X-Api-Key": apiKey } }],
}],
},
},
});Freestyle Transforms take this further in a few ways. First, we decoupled firewall grants from TLS routing and transforms. Then, we added other protocol-aware rules, including Postgres credential brokering, as well as inbound HTTP routing. HTTP headers can't be injected into raw TCP or IMAP traffic; those use different handling.
We focused on adaptable primitives. To spotlight my favorite: JSON transforms. Freestyle transformations allow you to patch the JSON body of outbound HTTP requests from the VM, granular on domain, path and method. They don't patch incoming requests, responses or arbitrary TCP traffic.
It lets you do
import { Freestyle } from "freestyle";
const providerKey = process.env.OPENAI_API_KEY!;
const freestyle = new Freestyle();
await freestyle.tls.rules.create({
action: "allow",
domain: "inference.local",
source: { vmId: "vm-123456" },
destination: { host: "api.openai.com", port: 443 },
match: { method: ["POST"], path: { exact: "/v1/responses" } },
transform: [
{ headers: { authorization: `Bearer ${providerKey}` } },
{ jsonPatch: [{ op: "add", path: "/model", value: "approved-model" }] },
],
});To inject your auth header, point a request at a destination and change the model being used, all without any OpenAI-specific connector. The VM still has to send a valid OpenAI request to https://inference.local/v1/responses.
What makes these APIs so great is that they solve the underlying problem rather than offering only point solutions, making them much more adaptable so you can do things like changing the model. Freestyle's APIs replace Sprite's OAuth lifecycle, managed billing and S3 signing with flexible primitives you can actually build on.
When we designed these APIs we took inspiration from the JSON Patch standard, Vercel and many other people, but created a system that lets you combine routing, header injection and JSON request patches.
For reference, here are some guides for how you can build a bunch of common use cases on these APIs:
- OpenAI models and authentication
- Anthropic models and authentication
- OpenRouter model routing and authentication
- PostHog credential injection
- Jev API credential injection
- Private Git repositories
- Neon Postgres credential injection
- Migrating from OpenShell
- Migrating from E2B
- Forward authentication for an inbound sandbox
Changing Tides
I’m going really hard on this API because it sucks. However, 10 years ago, it would’ve sucked a lot less. This design stands out to me as a design made without thinking enough about AI Agents from a company that says their primary use case is for AI Agents. When designing with AI Agents in mind, verbosity is OK. Agents can write boilerplate, but they can't create a missing primitive. This API feels distinctly like it was designed for an era where complexity and writing the code was the whole cost.
I notice this in the whole sprites API. Services as a whole primitive, exec serving a double meaning of pty and non pty, it seems very messy. The weirdest part about that is the Fly Machines API was an extremely well designed API. When we were designing our other networking APIs we looked there for inspiration. I think Freestyle now has the most powerful networking APIs in the sandbox space, and that came from building primitives people can build on instead of baking in every use case.
Designing APIs is hard, not for everyone and a learning experience. I hope this API can be a lesson for readers in what not to do.