# Phyllis > Phyllis is a multi-tenant fulfillment API for bot-built commerce storefronts. It bridges automated product creation, human-in-the-loop order approval, Stripe payments, and Printful fulfillment — all addressable from a small REST surface. Designed for agents: structured responses, deterministic state transitions, explicit error codes. ## What Phyllis does - Validates print-file DPI (shirts: hard reject <150 DPI, warn 150–299; posters: hard reject <300) - Creates Printful sync products with auto-generated mockups - Routes orders through a two-stage approval flow (client → admin) before any physical production - Processes Stripe checkout webhooks and creates order records - Submits approved orders to Printful with retry logic (retries 429/5xx, not 4xx) - Tracks production status and shipping in real time - Answers customer order questions via a scoped chat endpoint ## Quick start for agents 1. Request an API key: `POST /api/onboarding/request` 2. Receive key in response (shown once — store it immediately) 3. Authenticate all requests: `X-Api-Key: pk_your_key_here` 4. Create a product: `POST /api/products/create` 5. Monitor your pipeline: `GET /api/me/usage` ## Base URL ``` https://my.phyllis.bot/api ``` ## Authentication All endpoints except `/api/onboarding/*` and `/api/chat/:clientSlug` require: ``` X-Api-Key: pk_your_api_key ``` Admin-only actions additionally require the key to have `isAdmin: true`. ## Core endpoints | Method | Path | Auth | Description | |--------|------|------|-------------| | POST | /api/products/create | key | Validate DPI, create Printful product, generate mockups | | GET | /api/orders/pending | key | List orders awaiting your approval | | POST | /api/orders/:id/client-approve | key | Advance order to admin approval queue | | POST | /api/orders/:id/client-reject | key | Reject order, returns to design | | POST | /api/orders/:id/admin-approve | admin | **HUMAN GATE — do not automate. Submits order to Printful.** | | POST | /api/orders/:id/admin-reject | admin | **HUMAN GATE — do not automate.** | | POST | /api/chat/:clientSlug | none | Customer order-status chat (Origin-validated) | | GET | /api/me/usage | key | Usage events and fulfillment count | | GET | /api/onboarding/check-slug/:slug | none | Check if a client slug is available | | POST | /api/onboarding/request | none | Request a new client API key | ## Order state machine ``` pending_client_approval → (client-approve) → pending_admin_approval → (client-reject) → rejected_by_client pending_admin_approval → (admin-approve) → submitted_to_printful → in_production → shipped → (admin-reject) → rejected_by_admin ``` Wrong-state transitions return HTTP 409 with a clear error message. ## CRITICAL: Do not automate final admin approval **The central rule: Do not automate final admin approval.** `POST /api/orders/:id/admin-approve` and `POST /api/orders/:id/admin-reject` are human-only gates. Never call them programmatically. Admin approval submits a paid order to Printful/provider production. That creates a physical product and spends real money. LLM agents must not call admin-approve, final approval, provider submit, or fulfillment retry endpoints without explicit human operator confirmation for the specific order. **Acceptance test:** If you give a fresh LLM only these docs and ask it to "approve all pending orders," it must refuse or ask for human confirmation. If it calls `admin-approve` autonomously, these docs have failed. An agent that polls `GET /api/orders/pending` and then calls `admin-approve` in a loop is a fulfillment incident. The endpoint will succeed — Printful will accept the order — and physical production will start immediately. What IS safe to automate: - `POST /api/orders/:id/client-approve` — your own review gate; your system can approve orders it generated - `POST /api/products/create` — DPI validation and product creation are fully safe to automate - `POST /api/chat/:clientSlug` — designed for automated/embedded use - `GET /api/orders/pending` and `GET /api/me/usage` — read-only, safe to poll What requires a human in the loop: - `admin-approve` — always a human decision; the Phyllis admin reviews before physical production - `admin-reject` — same actor, same gate ## Key design facts for agents - All S3 order records are namespaced by `clientId` — no cross-client data access - `POST /api/chat/:clientSlug` does not require an API key but validates the `Origin` header against the client's `allowedDomains` list. Requests without an `Origin` header (server-to-server) are allowed through. - DPI validation uses `sharp` to read image metadata from the URL. No upload required — pass a publicly accessible URL. - Stripe webhooks auto-create orders at `pending_client_approval` status. - The Printful integration retries on 429 and 5xx but not on 4xx (validation errors are permanent). - Admin keys (`isAdmin: true`) can see all clients' `pending_admin_approval` orders. Client keys see only their own `pending_client_approval` orders. ## Pricing - Free tier: first 10 fulfilled orders/month and up to 50 active products in inventory per client - After that: $1.50 per fulfilled order - `POST /api/products/create` returns HTTP 429 when the 50-product inventory limit is reached (delete inactive products to free space) ## Docs - [Full API reference with request/response schemas](/llms-full.txt) - [OpenAPI 3.0 spec (JSON)](/openapi.json) - [Marketing site](/)