Agent Self-Signup: How an AI Agent Opens a Shager Sandbox in One Call, and Why a Human Still Holds the Publish Button
An agent can open its own Shager sandbox with one POST. Here is the call, what the sandbox allows, the human claim step, and how publish rights get granted.
7 min read

An AI agent can sign up for Shager by itself with one request: POST https://app.shager.ai/api/v1/agent/signup with a JSON body containing at least agent_name. It gets back an API key, an MCP URL and a claim link, and can immediately create workspaces, import media and draft posts. Nothing is scheduled or published until a human opens the claim link, signs in, and decides whether that key may publish.
That split is the whole design. The agent gets unblocked without waiting on a person; the person keeps the decision that touches real audiences.
Why let an agent sign itself up at all?
Because the alternative breaks the agent's flow. An agent asked to "set up our client's social distribution" hits a wall at the first step if the only way in is a human filling a sign-up form, verifying an email and creating a key in a dashboard. By the time someone does that, the agent's context is gone.
With self-signup the agent does the groundwork on its own: creates a workspace per client, pulls in the media, writes draft posts, and hands the human a single link with everything ready to review. The registration recipe is published for agents at /auth.md, and the agents page lists the discovery files an agent can read before it starts.
What does the signup call look like?
Only agent_name is required (2 to 80 characters). The other fields help the human recognize what they're claiming.
curl -s https://app.shager.ai/api/v1/agent/signup \
-H "Content-Type: application/json" \
-d '{
"agent_name": "Northwind content agent",
"contact_email": "ops@northwind-agency.example",
"organization_name": "Northwind Agency",
"purpose": "Draft weekly posts for 12 restaurant clients"
}'
The response wraps everything in data:
{
"data": {
"api_key": "shgr_live_...",
"organization_id": "...",
"sandbox": true,
"claim_url": "https://app.shager.ai/...",
"expires_at": "2026-09-19T10:04:11.000Z",
"mcp_url": "https://app.shager.ai/api/mcp",
"api_base": "https://app.shager.ai/api/v1",
"openapi_url": "https://app.shager.ai/api/v1/openapi.json",
"limits": { "workspaces": 5, "requests_per_minute": 120, "publishing": "after a human claims the sandbox" },
"next_steps": ["..."]
}
}
Two fields need immediate handling.
api_keyis shown once. Store it before doing anything else, in a secret store or an environment variable, never in a log line.claim_urlgoes to a human. If you passedcontact_email, Shager emails the claim link there too, but the agent should still surface it in its own reply.
Signups are limited to five per hour per address, which keeps a looping agent from spawning dozens of orphan sandboxes.

What can an unclaimed sandbox do?
| Allowed before the claim | Blocked until a human claims |
|---|---|
| Create up to 5 workspaces | Schedule or publish anything |
| Upload media from URLs | Create client connect links |
| Create draft posts | Send client approval requests |
| List and read everything it created |
Blocked calls fail with a clear error rather than a silent no-op. An attempt to schedule returns HTTP 403 with "code": "sandbox_unclaimed", and so do create_connect_link and request_approval. A sixth workspace returns 403 sandbox_limit. Good agents read the code and tell the human what's needed instead of retrying.
The reason connect links and approval requests are blocked is that both reach real people outside the agency: a client clicking a link to connect their Facebook Page, or a reviewer getting an approval email. That shouldn't happen on behalf of an organization nobody has verified.
A sensible first session in the sandbox
Here is what a well-behaved agent does in its first minutes, in Python with requests:
import os, requests
API = "https://app.shager.ai/api/v1"
H = {"Authorization": f"Bearer {os.environ['SHAGER_API_KEY']}"}
def call(method, path, **body):
r = requests.request(method, f"{API}{path}", headers=H, json=body or None, timeout=60)
payload = r.json()
if not r.ok:
err = payload["error"]
raise RuntimeError(f"{r.status_code} {err['code']}: {err['message']}")
return payload["data"]
me = call("GET", "/me")
print(me) # organization, scopes, sandbox status
ws = call("POST", "/workspaces", name="Harbor Tacos", timezone="America/Chicago")
media = call("POST", f"/workspaces/{ws['id']}/media",
url="https://harbortacos.example/press/fall-menu.jpg",
file_name="fall-menu.jpg")
print("Workspace", ws["id"], "media", media["id"], media["kind"])
Pass timezone explicitly when you create a workspace. It's an IANA zone name, and scheduling later is interpreted in that zone, so a restaurant in Chicago should not inherit someone else's default.
At this point there are no connected social accounts yet, because connecting accounts requires a connect link, which requires the claim. The agent prepares the content and the structure, and the human opens the door to the rest.
What happens when the human claims it?
The human opens claim_url, signs in to Shager (or creates an account), and takes ownership of the sandbox along with the workspaces, media and drafts the agent prepared. During the claim they choose whether the agent's key may also schedule and publish, which adds the publish scope.
That choice deserves a moment of thought. Leaving publish off is a perfectly good long-term setup: the agent drafts, the human or the client approves and schedules. Many agencies start there and add a separate publishing key later at https://app.shager.ai/dashboard/agents, where every key can be revoked and every agent call appears in the audit log. The tradeoffs between those setups are covered in the weekly distribution routine.
After the claim, the next steps the API itself suggests are straightforward: call create_connect_link so the account owner connects their Facebook, Instagram or YouTube without a Shager login, then create_post and, if the key has publish, schedule_post.
What if nobody claims it?
Unclaimed sandboxes expire after seven days, at the time given in expires_at. The drafts and workspaces go with them. An agent working on a longer project should remind the human before that date, and should not respond to expiry by signing up again and again. A fresh sandbox doesn't carry the old drafts.
How should an agent phrase the handoff?
Plainly, with the link and the consequences. Something like:
I set up a Shager sandbox for Northwind with 3 client workspaces and
9 draft posts. To connect the clients' accounts and publish, open this
link and sign in: <claim_url>
When you claim it you can choose whether I'm allowed to schedule posts
myself. If you don't claim it by Sep 19, the sandbox and drafts are deleted.
That message is honest about what was done, what's waiting, and what the human controls. It's also the message most likely to get clicked.
FAQ
Does self-signup cost anything?
No. The sandbox is free, and after the claim the organization lands on Shager's normal plans, where API and MCP access are included on every tier including Free. Plan limits are on the pricing page.
Can the agent use MCP instead of REST after signing up?
Yes. The response includes mcp_url, and the same key works as Authorization: Bearer shgr_live_... on the MCP server. The tools map one to one to the REST operations described in the OpenAPI document.
Can a claimed key ever publish without a human seeing the post?
Only if the human granted publish during the claim and the workspace doesn't require client approval. Both are the human's choices, and both can be changed later: keys can be revoked in the dashboard, and approval can be required per workspace.
Next step
Once the sandbox is claimed and accounts are connected, move from exploration to a full run. The end-to-end agent content pipeline picks up exactly where this leaves off, from brief to published posts and per-account clicks.