Skip to content
Shotcup

Platform: tenants, your artifact domain, statements

The Platform plan ($199 a month) is for products that run their own customers' code on Shotcup. Each of your customers can be a tenant: runs, sandboxes and uploads belong to it, it can have its own quotas within your plan, and you can read its usage for your own billing.

Create a tenant

curl https://shotcup.dev/api/v1/tenants \
  -H "Authorization: Bearer $SHOTCUP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"externalId": "cus_123", "name": "Acme Inc.", "metadata": {"crm": "4711"},
       "quotas": {"maxConcurrent": 5, "runsPerMonth": 100000, "computeHoursPerMonth": 10}}'

externalId is your own id for the customer (1-64 letters, digits and . _ : @ -), unique among your tenants. An account holds up to 1,000 tenants; more is a contract term. Metadata is up to 20 string entries for your own use.

Quotas are optional and each at most your plan's number: maxConcurrent (1-100), runsPerMonth (0-5,000,000), computeHoursPerMonth (0-300), memory ("standard" refuses the tenant's memory: "large" runs) and retentionDays (1-90; how long its runs' history and artifact links last, 90 days by default). A tenant without a quota shares your account's limit. A tenant's run always counts against your account's allowance too.

Run as a tenant

Two ways, and you can mix them:

  • The tenant header. Your account key with Shotcup-Tenant: cus_123 (the external id, or the tenant's id) acts for that tenant on the run, sandbox, upload and artifact routes. One key serves all your customers from your backend.
  • A tenant key. POST /api/v1/tenants/cus_123/keys with {"name": "production"} returns a key (shown once) that acts only for that tenant. Hand it to an environment you do not fully trust, such as your customer's own deployment. A tenant holds up to 10 live keys; an account creates up to 600 tenant keys an hour.

A tenant key, or a request naming a tenant, can run code, manage the tenant's sandboxes and workspace files, upload files and mint links to the tenant's own artifacts. It cannot read another tenant's runs, sandboxes, uploads or artifacts (they answer 404), nor the account: /api/v1/me, API keys and tenant management answer 403 tenant-forbidden. The MCP server takes account keys only. Your account key without the header sees every tenant's runs and sandboxes; each run and sandbox carries its tenantId.

Quota denials

A run past a tenant's own quota is a 429 whose error.kind names it and whose error.tenant is the external id, so you can tell your customer's limit from your plan's:

kindmeaning
tenant-concurrency-limitthe tenant's maxConcurrent runs are in flight
tenant-allowance-runsthe tenant used its runsPerMonth this month
tenant-allowance-computethe tenant used its computeHoursPerMonth this month
tenant-large-memory-not-allowedthe tenant is limited to memory: "standard"

Your plan's own denials (concurrency-limit, allowance-runs, ...) still apply to every tenant run. Months are UTC calendar months.

Usage, suspension and deletion

  • GET /api/v1/tenants lists tenants with this month's runs and compute (?limit= up to 100, ?cursor= for the next page).
  • GET /api/v1/tenants/cus_123/usage?month=2026-10 returns one tenant's runs and compute-hours in a month, for billing your customer.
  • PATCH /api/v1/tenants/cus_123 changes name, metadata (replaced whole), quotas (merged; null removes one) and suspended. A suspended tenant's keys and header answer 403 tenant-suspended and its artifact links 404 until you resume it.
  • DELETE /api/v1/tenants/cus_123 revokes its keys, deletes its saved sandboxes and frees the external id; its run history stays until it expires and still counts in your usage.

The dashboard's Platform section does all of this too.

Artifact links normally live on shotcup.page. A Platform account can serve them from one host of its own, such as artifacts.example.com (a subdomain; more hosts are a contract term):

  1. POST /api/v1/domains with {"host": "artifacts.example.com"}. The answer lists the DNS records to set: a TXT record at _shotcup-challenge.artifacts.example.com that proves the host is yours, and a CNAME that points the host at Shotcup.
  2. POST /api/v1/domains/artifacts.example.com/verify once the records are live. The domain becomes verified; Shotcup then attaches it to its hosting, and it becomes active.
  3. From then on, artifact links of your runs and your tenants' runs use https://artifacts.example.com/a/.... Links issued before keep working.

Your host serves only your account's artifacts, with the same protections as shotcup.page: no cookies, X-Content-Type-Options: nosniff, no referrer, and HTML and SVG in a sandbox without your origin. Everything else on it answers 404. DELETE /api/v1/domains/artifacts.example.com removes it.

Monthly statements and invoices

  • GET /api/v1/statements lists months; GET /api/v1/statements/2026-10 returns the month as JSON with your plan fee, usage, overage and a line per tenant plus your own usage; ?format=csv and ?format=html (a printable statement) too, and ?tenant=cus_123 for one tenant's usage. The current month is a running statement; a month is final from 01:00 UTC on the 1st of the next.
  • PUT /api/v1/billing/details sets the legal name, billing email, address and VAT or tax ID printed on your invoices.
  • Platform is invoiced monthly in arrears (the plan fee plus the month's overage, net 30) from the final statement, once invoicing is switched on for your account.

This page as Markdown:/docs/platform.md