--- url: /guide.md description: >- What Platypus does for a venue — the admin at a glance, and where each feature is documented. --- # What Platypus does Platypus sells tickets for small theatre venues. You get public event pages on `platypus.tickets` under your own venue address (`/t/{your-slug}/`), an admin for running them, and checkout that pays into **your own Stripe account** — Platypus never holds your money. ## Your admin Sign in at `https://platypus.tickets/t/{your-slug}/auth/login` with your admin email. Forgot the password? The reset link on that page emails you a new one. The dashboard shows what's coming up and what needs doing; everything else hangs off the sidebar: | Area | What it's for | | --- | --- | | **Events** | Create and manage events — dates, description, images, and ticket types. [Setting up an event](/guide/setting-up-an-event) | | **Tickets** | Sales tallies per event, issuing tickets yourself (including comps), printable door sheets, PDF re-downloads | | **Venues** | Your directory of performance venues, used when scheduling multi-date events | | **Mailing list** | Subscribers, tags, campaigns, and a member benefit (discount) for your list | | **Payments** | Connect your Stripe account so checkout works. [Stripe payments setup](/guide/stripe-payments) | | **Settings** | Organisation details, Site Domain (for embeds), contact details, mail provider, appearance | | **Account & Billing** | Your Platypus subscription. [Subscription & billing](/guide/subscription-and-billing) | ## Your public pages Visitors see your events at `https://platypus.tickets/t/{your-slug}/events` — a listing of everything published, each with its own detail page and (once Stripe is connected) a **Buy tickets** button. The listing and per-event cards are built to be [embedded in your own website](/api/embeds), so ticket sales live where your audience already is. ## The two prerequisites for selling 1. **Connect Stripe** — checkout is hidden until your Stripe keys are saved and verified. Five minutes: [Stripe payments setup](/guide/stripe-payments). 2. **An event with published tickets** — at least one ticket type with capacity, on a published event: [Setting up an event](/guide/setting-up-an-event). Your [Platypus subscription](/guide/subscription-and-billing) bills your platform access — it is deliberately **not** a switch on ticket sales. ## Not written yet This guide is young. Topics we know it still needs are listed honestly at [Planned topics](/guide/planned) rather than padded out with half-written pages. --- --- url: /guide/setting-up-an-event.md description: >- Creating an event — metadata, description, single or multiple dates, and ticket types with pricing and capacity. --- # Setting up an event **Events → New event** opens a three-tab form: **Event information**, **Event description**, and **Tickets**. Save lives in the top bar — it appears once you've changed something. ## Event information * **Title** and **category** — the category is your own label (e.g. *Drama*, *Comedy*, *Kids*) shown on the listing. * **Date mode** — one date or several: * **Single**: one date and start time. * **Multiple**: add a stop per performance date, each with its own time label (e.g. *7:30 PM*) and location label. Stops can pull a venue from your **Venues** directory — picking one snapshots its details onto the stop. Up to 100 stops. * **Location** — the venue/location line shown to visitors (single-date events). * **Event image** — JPG/PNG/GIF/WebP up to 500 KB, shown on the listing and detail page. Uploading the same image twice reuses the stored copy. * **Visibility**: * **Publish status** — *draft* keeps the event invisible to the public; *published* puts it on your listing. * **Private event** — published but unlisted: reachable only by its link, with an optional **event password**. Private events never appear on your public listing or in the API. * **Over 18** — flags age restriction on the public page. * **Reminder** — optionally email ticket-holders a reminder a chosen number of days (0–30) before the event. The public page URL comes from the title and date (e.g. `/t/nbt/events/the-long-run-2030-04-05`). Once you publish, the URL is locked — retitling later doesn't break links you've already shared. ## Event description A rich-text editor for the full pitch — formatting and inline images are fine (up to 20,000 characters). It's shown on the event's public page and returned via the API, so keep it self-contained. ## Tickets Tickets are sold in **ticket types** (batches) you define per event — for example *General admission* $25 and *Child* $15, or a free ticket. Each type carries: * **Price** (AUD, or mark it **free**) and an optional **surcharge** — buyers see the combined price. * **Capacity** (*ticket limit*) — the number available. Checkout enforces it: once they're gone, they're gone, including while unpaid orders briefly hold seats. * **Order limit** — the most one order can buy, if you want to prevent bulk purchases. * **Publish status** — a *draft* type is invisible to buyers; publish it when you're ready to sell. Publishing the event prompts you about still-draft ticket types so nothing stays accidentally unsellable. * On multi-date events, each type belongs to a **specific stop** — use *copy to all dates* to fan a type out across every performance. Type names must be unique per date. ## Before it can sell A published event with published ticket types still needs your [Stripe connection](/guide/stripe-payments) before the **Buy tickets** button appears. Everything else — the event page, the listing, embeds — works without it. --- --- url: /guide/subscription-and-billing.md description: >- Your Platypus subscription — plans, subscribing, managing payment and invoices, and what the subscription does (and doesn't) control. --- # Subscription & billing Platypus bills your venue a subscription for platform access, plus **$1 AUD per ticket sold**. This page is about paying Platypus; taking money from your ticket buyers is separate — that's [Stripe payments setup](/guide/stripe-payments). ## Plans | Plan | What you pay | | --- | --- | | **Monthly** | A flat base fee each month, plus $1 AUD per ticket sold | | **Yearly** | $50 AUD per year, plus $1 AUD per ticket sold (ticket fees still billed monthly) | Exact base pricing is shown on the subscribe screen before you commit. There's no trial period — the first invoice is charged when you subscribe. ## Subscribing **Settings → Account and Billing** shows your subscription state. If you're not subscribed, pick a plan and **Subscribe** — you'll be taken to a Stripe-hosted checkout to enter card details, then returned to the billing page. (If the panel says billing isn't configured yet, that's on the Platypus operator, not you.) ## Managing it The **billing portal** button hands you to Stripe's hosted portal for everything ongoing: change card, view and download invoices, or cancel. Cancellation runs to the end of the paid period. ## What the subscription controls — honestly The subscription pays for your platform access. It is **not** a switch on ticket sales: * Ticket checkout works when your [Stripe keys are configured](/guide/stripe-payments) — that's the only technical gate on selling. * The $1/ticket fee is metered against your subscription as tickets sell. * If your venue's Platypus account is ever deactivated (an operator action, not an automatic consequence of a lapsed card), your public pages and admin return "not found" until it's reactivated — in-flight payments still complete and issue tickets. If you're winding down, cancel the subscription **and** talk to the operator about the account itself. --- --- url: /guide/stripe-payments.md description: >- Connecting your Stripe account so Platypus can sell tickets on your behalf — keys, webhook, test mode, go-live checklist, and troubleshooting. --- # Stripe payments setup Ticket money goes **straight to your venue's own Stripe account** — Platypus initiates the payment with your keys and never holds your funds. Until Stripe is connected here, visitors can see your events but the **Buy tickets** button stays hidden. You'll need a [Stripe account](https://stripe.com) with its business details activated (a free account in test mode is enough to rehearse). Open **Payments** in the admin sidebar; the page walks you through three steps — about five minutes. ## 1. Create a restricted key in Stripe In your Stripe dashboard: **Developers → API keys → Create restricted key**. A restricted key can only do the things ticket sales need, which makes it much safer than a full secret key. Grant exactly: | Permission | Level | Why | | --- | --- | --- | | Payment Intents | Write | takes payments | | Account | Read | lets Platypus confirm the connection and show your account name | | Webhook Endpoints | Write | lets Platypus set up payment confirmation for you | Leave everything else on **None**. (A full secret key `sk_live_...` works too, but restricted is recommended — it limits what could go wrong if the key ever leaked.) ## 2. Paste your keys * **Publishable key** — `pk_live_...` (or `pk_test_...` while testing). Safe to share; it only identifies your account. * **Secret key** — the restricted key you just created (`rk_live_...`). Stored encrypted, never shown again. Platypus verifies the secret key against Stripe when you save — a rejected key is never stored, and a successful save shows your Stripe account name on the page. Both keys must be from the same mode (both live, or both test). ## 3. Set up payment confirmation (webhook) When a payment goes through, Stripe tells Platypus so the tickets are issued and emailed. That link is a webhook: * **Automatic (recommended):** click **Set up automatically**. Platypus creates the endpoint on your Stripe account and stores its signing secret. Done. * **Manual** (if your key lacks Webhook Endpoints permission): in Stripe, **Developers → Webhooks → Add endpoint**; paste the endpoint URL shown on the Payments page, select exactly the events `payment_intent.succeeded` and `payment_intent.canceled`, then copy the endpoint's signing secret (`whsec_...`) back into the Payments page. A missing webhook **warns but doesn't block sales** — payments still verify at the end of checkout. Set it up anyway: it's the safety net that issues tickets if a buyer's connection drops at the wrong moment. ## 4. Verify The status card shows the connected account, a **LIVE**/**TEST MODE** badge, and the webhook state. **Test connection** re-checks any time without re-entering keys. ## Test keys vs live keys Rehearse with your Stripe **test** keys (`rk_test_`/`pk_test_`) — checkout then accepts only [Stripe's test cards](https://docs.stripe.com/testing) such as `4242 4242 4242 4242`. Buy a ticket yourself and confirm the order completes and the ticket PDF arrives by email. **Switching between test and live keys clears the stored webhook** (Stripe webhooks are per-mode) — re-run **Set up automatically** after the switch. ## Go-live checklist * \[ ] Live restricted key (`rk_live_`) and live publishable key (`pk_live_`) saved * \[ ] Webhook state shows **Automatic ✓** (or **Manual ✓**) — re-done after the test→live switch * \[ ] **Test connection** shows your account name with a **LIVE** badge * \[ ] **Buy tickets** button visible on a public event page ## Troubleshooting | Symptom | Likely cause | Fix | | --- | --- | --- | | No **Buy tickets** button on the event page | Keys missing, or event has no purchasable capacity | Complete steps 1–2; check the event's ticket types are published with capacity | | Save rejected with a Stripe error naming a permission | Restricted key missing that permission | Edit the key in Stripe (Developers → API keys), add it, then **Test connection** | | **Set up automatically** fails with a permission message | Key lacks Webhook Endpoints: Write | Add the permission, or use the manual path in step 3 | | Real card declined at checkout | Test keys still configured | Save your live keys, re-run webhook setup | | Payment succeeded but tickets not confirmed / webhook errors in Stripe | Wrong or stale signing secret, wrong endpoint URL, or wrong events | Re-run **Set up automatically**; if manual, confirm the URL and the two exact events, re-paste the `whsec_` | | **Test connection** fails after previously working | Key rolled or deleted in Stripe | Create a new key and save it on the Payments page | | "Stored key unreadable — re-enter your keys" | Platform-side key rotation | Paste your keys again | | Webhook auto-setup reports an endpoint limit | Stripe's 16-endpoints-per-account cap | Delete unused endpoints in Stripe (Developers → Webhooks) and retry | --- --- url: /guide/planned.md description: Admin guide topics that exist in the product but aren't written up yet. --- # Planned topics These features are live in the product; their guide chapters aren't written yet. We'd rather list them honestly than pad the guide with half-pages. Until then, the admin screens themselves are designed to explain as they go. **Events & tickets** * Event lifecycle — cancelling with a public reason, restoring, duplicating, and what buyers see * Tickets admin — sales tallies, issuing tickets yourself (including complimentary), the group PDF with QR codes, re-downloads * The door sheet — printable attendee list for front-of-house **Audience** * Mailing list — subscribers, CSV import/export, and double opt-in * Tags and campaigns — segmenting your list and sending mail-outs * Member benefit — a discount for people on your list, and how it appears at checkout * Phone list capture * Contact form and the Contact Form Inbox **Settings** * Mail settings — sending from your own domain (Mailgun, Resend, SendGrid, SMTP) and the test-send button * Appearance — public site theme and custom CSS hooks * Timezone Something missing that isn't listed? The event's contact form reaches a real person. --- --- url: /api.md description: >- The Platypus.Tickets public event API — base URL, response format, what is and isn't supported, and CORS rules. --- # Introduction Platypus exposes a small, read-only public API for event data. It exists so you can show a venue's events on the venue's own website — typically feeding a custom listing, or alongside the [iframe embeds](/api/embeds). ## Base URL Every endpoint lives under the venue's tenant prefix: ``` https://platypus.tickets/t/{slug}/api/ ``` `{slug}` is the venue's tenant slug — the same one that appears in its public event pages (for example `nbt` in `https://platypus.tickets/t/nbt/events`). There is **no authentication**. The API returns only data that is already public on the venue's event pages. ## Response format Responses use a JSON:API-**style** document structure — resources carry `type`, `id`, `attributes`, and `links`, attribute names are `snake_case`, and collection responses carry top-level `data`, `links`, and `meta`. It is deliberately not a full [JSON:API](https://jsonapi.org/) implementation. To save you reading the spec against reality: | JSON:API feature | Supported? | | --- | --- | | `type` / `id` / `attributes` / `links.self` resource shape | Yes | | `application/vnd.api+json` media type | No — responses are plain `application/json` | | `?include=` compound documents | No — occurrences are always embedded inline | | `?filter[...]=` | No | | `?sort=` | No | | `?page[size]=` | No — page size is fixed at 12 | | `?page=` (Laravel-style page number) | Yes, on the index endpoint | ## Pagination The index endpoint returns 12 events per page with Laravel's standard pagination envelope: `links.first/last/prev/next` and `meta.current_page/last_page/per_page/total` (plus `meta.links` for rendering pagers). Request further pages with `?page=2`. ## Visibility The API applies the same visibility rules as the public event listing: * Only **published** events appear. Drafts never do. * **Private** events (unlisted, password-protected pages) never appear — not in the index, not by id. * **Cancelled** events remain visible with `publish_status: "cancelled"` and a `cancellation_reason`, unless the venue chose to hide the cancelled listing — then the API returns them in neither the index nor the show endpoint (the show endpoint responds `404`). * Past-dated events drop off the index. ## CORS Browsers may call the API from exactly two origins: the Platypus site itself, and the venue's own configured website domain — the **Site Domain** in the venue's admin settings (served as `https://{site-domain}`). Requests from other origins get no `Access-Control-Allow-Origin` header and will be blocked by the browser. Each venue's allowance applies only to its own `/t/{slug}/` paths. If you're building for a venue and hitting CORS errors, the venue admin needs to set the Site Domain to the domain your page is served from — see [Embedding on your site](/api/embeds#site-domain). ## Endpoints Two endpoints exist today: | Method | Path | Purpose | | --- | --- | --- | | `GET` | `/t/{slug}/api/events` | Published, listed events (paginated) | | `GET` | `/t/{slug}/api/events/{id}` | One event by numeric id | Full detail and real request/response examples: [Event endpoints](/api/events). ::: info These examples can't drift The JSON examples in this section are fixture files asserted byte-for-byte by the Platypus test suite against real API responses. If the API changes shape, the build fails until the examples are regenerated — what you read here is what the wire carries. ::: --- --- url: /api/events.md description: >- The two public event endpoints — list events and fetch one event — with real request and response examples. --- # Event endpoints Both endpoints are read-only `GET`s, unauthenticated, and scoped to one venue by the `{slug}` path segment. The JSON below comes from test-asserted fixtures — it is real API output, not hand-maintained. ## List events ``` GET /t/{slug}/api/events ``` Returns published, publicly listed events, 12 per page, with the pagination envelope described in the [introduction](/api/#pagination). ```bash curl -s https://platypus.tickets/t/nbt/api/events ``` ```json { "data": [ { "type": "events", "id": "1", "attributes": { "title": "An Evening of One-Act Plays", "slug": "an-evening-of-one-act-plays-2030-03-14", "category": "Drama", "event_date": "2030-03-14T00:00:00.000000Z", "event_time": "19:30:00", "date_mode": "single", "occurrence_count": 0, "next_occurrence": null, "occurrences": [], "description": "Four short plays from local writers, performed back to back with one interval.", "location": "Main Stage", "over_18": false, "publish_status": "published", "tickets_enabled": true, "show_images": true, "small_image_path": null, "cancellation_reason": null, "cancelled_at": null }, "links": { "self": "https://platypus.tickets/t/nbt/api/events/1" } }, { "type": "events", "id": "2", "attributes": { "title": "The Long Run", "slug": "the-long-run-2030-04-05", "category": "Comedy", "event_date": "2030-04-05T00:00:00.000000Z", "event_time": "19:30:00", "date_mode": "multiple", "occurrence_count": 3, "next_occurrence": { "occurrence_date": "2030-04-05", "time_label": "7:30 PM", "location_label": "Main Stage", "status": "scheduled" }, "occurrences": [ { "occurrence_date": "2030-04-05", "time_label": "7:30 PM", "location_label": "Main Stage", "status": "scheduled" }, { "occurrence_date": "2030-04-06", "time_label": "7:30 PM", "location_label": "Main Stage", "status": "scheduled" }, { "occurrence_date": "2030-04-07", "time_label": "2:00 PM", "location_label": "Main Stage", "status": "scheduled" } ], "description": "A three-night season of our award season favourite.", "location": "Main Stage", "over_18": false, "publish_status": "published", "tickets_enabled": true, "show_images": true, "small_image_path": null, "cancellation_reason": null, "cancelled_at": null }, "links": { "self": "https://platypus.tickets/t/nbt/api/events/2" } } ], "links": { "first": "https://platypus.tickets/t/nbt/api/events?page=1", "last": "https://platypus.tickets/t/nbt/api/events?page=1", "prev": null, "next": null }, "meta": { "current_page": 1, "from": 1, "last_page": 1, "links": [ { "url": null, "label": "« Previous", "page": null, "active": false }, { "url": "https://platypus.tickets/t/nbt/api/events?page=1", "label": "1", "page": 1, "active": true }, { "url": null, "label": "Next »", "page": null, "active": false } ], "path": "https://platypus.tickets/t/nbt/api/events", "per_page": 12, "to": 2, "total": 2 } } ``` ## Get one event ``` GET /t/{slug}/api/events/{id} ``` `{id}` is the numeric event id — use the `id` from the index response, or the `links.self` URL directly. Unknown ids, drafts, private events, and hidden-cancelled events all return `404`. ```bash curl -s https://platypus.tickets/t/nbt/api/events/1 ``` ```json { "data": { "type": "events", "id": "1", "attributes": { "title": "An Evening of One-Act Plays", "slug": "an-evening-of-one-act-plays-2030-03-14", "category": "Drama", "event_date": "2030-03-14T00:00:00.000000Z", "event_time": "19:30:00", "date_mode": "single", "occurrence_count": 0, "next_occurrence": null, "occurrences": [], "description": "Four short plays from local writers, performed back to back with one interval.", "location": "Main Stage", "over_18": false, "publish_status": "published", "tickets_enabled": true, "show_images": true, "small_image_path": null, "cancellation_reason": null, "cancelled_at": null }, "links": { "self": "https://platypus.tickets/t/nbt/api/events/1" } } } ``` ## Attributes | Attribute | Type | Notes | | --- | --- | --- | | `title` | string | Event title | | `slug` | string | URL slug of the public event page (`/t/{slug}/events/{event-slug}`) | | `category` | string | Venue-chosen category label (e.g. `Drama`) | | `event_date` | string | Event date as a datetime string. For `multiple`-mode events this mirrors the current/next stop and advances as stops pass | | `event_time` | string | `HH:MM:SS`. Mirrors the current stop for `multiple`-mode events | | `date_mode` | string | `single` or `multiple` | | `occurrence_count` | integer | Number of stops. Always `0` for `single`-mode events | | `next_occurrence` | object | null | Compact occurrence the venue is pointing at now (see below). Always `null` for `single` mode | | `occurrences` | array | All stops in date order (see below). Always `[]` for `single` mode | | `description` | string | Event description; may contain HTML authored in the admin editor | | `location` | string | Venue/location label | | `over_18` | boolean | Age-restriction flag | | `publish_status` | string | `published` or `cancelled` (drafts are never returned) | | `tickets_enabled` | boolean | Whether the event sells tickets through Platypus | | `show_images` | boolean | Whether the venue wants event imagery shown | | `small_image_path` | string | null | Path of the event image, or `null` | | `cancellation_reason` | string | null | Set when `publish_status` is `cancelled` | | `cancelled_at` | string | null | ISO-8601 timestamp of cancellation | ## Multi-date events When a venue schedules an event across several dates (`date_mode: "multiple"`), every stop is embedded inline — there is no separate occurrences endpoint. Each compact occurrence carries `occurrence_date` (`YYYY-MM-DD`), `time_label`, `location_label`, and `status` (`scheduled` or `cancelled`). `next_occurrence` points at the nearest upcoming stop, and the flat `event_date`/`event_time` mirror it. ```bash curl -s https://platypus.tickets/t/nbt/api/events/2 ``` ```json { "data": { "type": "events", "id": "2", "attributes": { "title": "The Long Run", "slug": "the-long-run-2030-04-05", "category": "Comedy", "event_date": "2030-04-05T00:00:00.000000Z", "event_time": "19:30:00", "date_mode": "multiple", "occurrence_count": 3, "next_occurrence": { "occurrence_date": "2030-04-05", "time_label": "7:30 PM", "location_label": "Main Stage", "status": "scheduled" }, "occurrences": [ { "occurrence_date": "2030-04-05", "time_label": "7:30 PM", "location_label": "Main Stage", "status": "scheduled" }, { "occurrence_date": "2030-04-06", "time_label": "7:30 PM", "location_label": "Main Stage", "status": "scheduled" }, { "occurrence_date": "2030-04-07", "time_label": "2:00 PM", "location_label": "Main Stage", "status": "scheduled" } ], "description": "A three-night season of our award season favourite.", "location": "Main Stage", "over_18": false, "publish_status": "published", "tickets_enabled": true, "show_images": true, "small_image_path": null, "cancellation_reason": null, "cancelled_at": null }, "links": { "self": "https://platypus.tickets/t/nbt/api/events/2" } } } ``` --- --- url: /api/embeds.md description: >- Embedding Platypus event details, ticket sales, and your events listing on your own website with iframes. --- # Embedding on your site Platypus pages are built to live inside your own website. Three surfaces embed with a plain ` ``` The event slug is the `slug` attribute from the [API](/api/events#attributes), also visible in the event page URL. Pick a `height` that fits the content; the cards don't auto-resize the frame. ## Buying tickets inside the frame The tickets card's **Buy tickets** button navigates the iframe itself to the checkout, which is designed to complete in-frame — card entry included. Buyers never need to leave your page. (Checkout requires the venue's Stripe keys to be configured; until then the buy button isn't shown.) ## Theming There are no per-embed style parameters. Embeds follow the venue's global appearance settings — public site theme and custom CSS — configured in the Platypus admin, so they match the venue's Platypus pages everywhere they're shown. ## Prefer data over iframes? If you want full control of the markup, fetch the same event data as JSON from the [public API](/api/events) and render it yourself. The [CORS allowance](/api/#cors) uses the same Site Domain setting. --- --- url: /faq.md description: >- What Platypus.Tickets is, who it's for, and how it's different from marketplace ticketing platforms. --- # About Platypus **Platypus.Tickets sells tickets for small theatre venues.** It was built for the 50-seat end of the world — venues where Eventbrite-style marketplaces are the wrong shape and the wrong price. ## What makes it different * **Your audience stays yours.** Platypus lives quietly under your own website — event listings and ticket cards embed straight into your pages. There's no marketplace, no "you might also like", no ads, no urgency banners. * **Your money goes to you.** Checkout charges the buyer through *your venue's own Stripe account*. Platypus never holds your funds. More in [Stripe and payments](/faq/stripe). * **Flat, honest pricing.** A flat subscription plus $1 AUD per ticket sold. No percentage skim, no booking-fee games passed to your audience. * **No accounts for buyers.** Someone buying a ticket picks their seats' worth of tickets, pays, and gets a PDF by email. No sign-up wall. ## Who runs it Platypus is small, Australian-built software (currency is AUD throughout), run for a small number of venues rather than growth-at-all-costs. It's honestly MVP in places — what's built and what's coming is laid out plainly in [Features & roadmap](/faq/features). ## Where are the docs for running a venue? Venue operators: see the [Guide](/guide/). Developers embedding events into a venue site: see the [API reference](/api/). --- --- url: /faq/stripe.md description: >- How Platypus uses Stripe — venue-owned payments, what buyers see, and what venues need. --- # Stripe and payments ## Why Stripe? Card payments are serious infrastructure — vaulting card numbers, 3-D Secure, fraud screening, payouts. Platypus doesn't reinvent any of that: every payment is processed by [Stripe](https://stripe.com), and specifically by **the venue's own Stripe account**. ## What that means for buyers When you buy a ticket, your card details go to Stripe, never to Platypus, and the charge is made by the venue itself — it's the venue's name on your statement, the venue's refund to give. Checkout happens right on the event page (or inside the venue's own website when embedded), card step included, and your tickets arrive by email as a PDF. ## What that means for venues * You need a Stripe account with charges enabled — free to create; Stripe's per-transaction fees apply as with any Stripe business. * You paste restricted API keys into the Platypus admin and Platypus verifies them before anything is stored (encrypted). Setup is a five-minute guided page: [Stripe payments setup](/guide/stripe-payments). * Refunds are handled in your Stripe dashboard today — you have full merchant control. * Stripe's test mode works end to end, so you can rehearse a purchase with a test card before going live. ## Does Platypus touch the money? No. Ticket revenue settles from Stripe to the venue's bank account directly. Platypus bills the venue separately — a flat subscription plus $1 AUD per ticket sold ([details](/guide/subscription-and-billing)). ## What's coming A hosted onboarding option (Stripe Connect) is on the roadmap — venues would click through Stripe-hosted setup instead of creating and pasting keys, and gain in-app refunds. Current status: [Features & roadmap](/faq/features). --- --- url: /faq/features.md description: 'What Platypus can do today, and what''s on the roadmap.' --- # Features & roadmap Platypus is honestly MVP: what's listed as built is genuinely live; what isn't, isn't dressed up to look finished. ## Built today **For audiences** * Public event pages under the venue's address — listing, detail pages, no account needed * Ticket checkout on the event page (or embedded in the venue's site), paying the venue's own Stripe account * Tickets emailed as a PDF with QR codes; cancelled events show the reason plainly **For venues** * Event management — single or multi-date events, rich descriptions, images, drafts, private (unlisted, password-protectable) events * Ticket types with pricing, surcharges, capacity, and per-order limits; capacity enforced at checkout * Box-office tools — issue tickets manually (including comps), printable door sheets, PDF re-downloads * Mailing list with double opt-in, tags, campaigns, and a member discount * Contact form, custom mail provider, appearance theming and custom CSS * Website embedding — iframe event cards and listing, plus a read-only [JSON API](/api/) ## On the roadmap * **Hosted Stripe onboarding (Stripe Connect)** — click-through payment setup instead of pasting API keys, and refunds from inside the Platypus admin * **QR check-in at the door** — scan tickets on a phone, see who's arrived * **Venue self-service sign-up** — see [Sign-ups](/faq/sign-ups) Roadmap items ship when they're ready; nothing above is a promise with a date on it. --- --- url: /faq/sign-ups.md description: 'How venues get onto Platypus today, and the state of self-service sign-up.' --- # Sign-ups **Short version: sign-ups are coming; today, venues join by talking to us.** Platypus doesn't have a self-service "create your venue" button yet. New venues are set up by the operator — you agree a venue address (the `slug` in `platypus.tickets/t/{slug}/`), we provision the account, and you get a one-time login to take from there. From that point everything is yours to drive: [first login through first sale](/guide/). That's a deliberate stage, not a velvet rope — Platypus is being grown carefully with a small number of venues while the product fills out. Interested in running your venue on Platypus? Get in touch through an existing Platypus venue's contact page, or watch this page — self-service sign-up will land here when it's real. --- --- url: /faq/about-these-docs.md description: >- How this documentation stays true to the running system — build stamps, test-backed examples, and coverage gates. --- # About these docs This site lives at `docs.platypus.tickets`, built and deployed straight from the same repository as the product itself. It's reference documentation, not marketing. A few mechanics keep it honest: * **It documents current behavior.** Where the product has a quirk (say, [what your subscription does and doesn't control](/guide/subscription-and-billing#what-the-subscription-controls--honestly)), the docs say so rather than describing the system we'd like to have. * **API examples can't drift.** Every JSON example in the [API section](/api/) is a fixture file asserted byte-for-byte against real API responses by the Platypus test suite. If the API changes, the tests fail until the examples are regenerated. * **Coverage is enforced.** Each documented feature is tied to the capability specs that define Platypus. A new capability can't ship without either a docs page or an explicit, justified exemption — the docs build fails otherwise. * **Every build says what it documents.** The footer (and `/build-info.json`) carries the exact source commit these pages were built from. * **LLM-friendly.** The full content is published as [`/llms.txt`](/llms.txt) and [`/llms-full.txt`](/llms-full.txt) for AI tools to consume. Found something that doesn't match the product? That's a bug in the docs — report it via a venue's contact page.