Marketplace API reference
Every stable public endpoint on the marketplace API. Bearer-token authenticated (JWT or long-lived API key). Rate-limited. Third-party-integration-safe.
Base URL: https://railcall-marketplace-lggm.onrender.com
Every authenticated endpoint below accepts EITHER a JWT access token (from POST /auth/login) OR a long-lived API key (rc_ak_live_… minted at /marketplace/settings/api-keys). Header: Authorization: Bearer <token>.
Rate limits: 60 req/min per IP baseline. Some endpoints tighter (login/signup: 8/min; publish: 5/hour; design-partner: 3/hour).
Auth
/auth/registerauth: noneCreate a new marketplace account.
{
"email": "you@company.com",
"password": "at-least-12-chars-with-a-digit-1"
}{
"access_token": "<JWT>",
"refresh_token": "<opaque>",
"user": { "id": "...", "email": "...", "email_verified": false, "is_admin": false }
}/auth/loginauth: noneSign in with email + password.
{ "email": "you@company.com", "password": "..." }{ "access_token": "...", "refresh_token": "...", "user": {...} }/auth/refreshauth: refresh token in bodyRotate the refresh token, get a fresh access token.
{ "refresh_token": "..." }{ "access_token": "...", "refresh_token": "...", "user": {...} }/auth/meauth: JWTCurrent user profile. Powers dashboard hydration + SSO complete page.
{ "id": "...", "email": "...", "email_verified": true, "is_admin": false }/auth/api-keysauth: JWT (API keys refused)List your long-lived API keys. Never returns the secret — only prefix + name + last-used.
[
{
"id": "...",
"name": "prod CI runner",
"prefix": "rc_ak_live_A",
"scopes": "publish read",
"last_used_at": "2026-07-25T00:12:33Z",
"revoked_at": null,
"expires_at": null,
"created_at": "2026-07-24T18:44:02Z"
}
]/auth/api-keysauth: JWT (API keys refused — lateral-privilege guard)Create a long-lived API key. Secret returned exactly once in the response — save it now.
{ "name": "prod CI runner", "scopes": "publish read" }{
"id": "...",
"name": "prod CI runner",
"prefix": "rc_ak_live_A",
"scopes": "publish read",
"expires_at": null,
"created_at": "2026-07-25T02:01:14Z",
"secret": "rc_ak_live_A1b2c3D4e5F6g7H8i9J0k1L2"
}/auth/api-keys/:idauth: JWTRevoke an API key. Any CI job using it 401s on next request.
{ "ok": true }Listings (marketplace catalog)
/listingsauth: optional JWT (unauth returns public only)Browse the catalog. Anonymous callers see only public listings; authenticated members additionally see org_internal listings owned by orgs they belong to.
listing_type— workflow | policy_pack | prompt_library | modulecategory— one of the LISTING_CATEGORIES enumq— search string (matches title / slug / description)limit— 1-100 (default 50)offset— for pagination
{ "count": 50, "total": 137, "offset": 0, "limit": 50, "items": [ ... ] }/listings/:idOrSlugauth: optional JWTFull detail for a listing. 404s on org_internal listings the caller isn't authorized to see (deliberate — prevents slug enumeration).
{
"id": "...", "slug": "sami666/salesforce", "title": "...", "description": "...",
"listing_type": "module", "price_cents": 19900, "pricing_model": "subscription",
"billing_interval": "month", "publisher_pubkey": "...", "seller": {...}, ...
}/listingsauth: JWT OR API key (BearerAuthGuard)Publish a signed listing. Rate-limited 5/hour/seller. Requires a registered publisher pubkey + valid signature.
{
"id": "your-handle/your-module",
"listing_type": "module",
"title": "...",
"description": "at least 20 chars",
"category": "Modules",
"price_cents": 0,
"payload": { ... },
"payload_sha": "<sha256 hex>",
"publisher_pubkey": "<64 hex>",
"publisher_sig": "<128 hex>",
"created_at": "2026-07-25T12:00:00Z",
"visibility": "public"
}{ "id": "cuid", "slug": "your-handle/your-module", "listing_type": "module", ... }Organization (RBAC + billing surfaces)
/orgsauth: JWTSelf-serve org creation. Caller becomes the org's first owner. Refuses if caller is already a member of an org.
{ "slug": "acme", "name": "Acme Inc", "email_domains": ["acme.com"], "billing_email": "billing@acme.com" }{ "id": "...", "slug": "acme", "name": "Acme Inc", "email_domains": [...], "created_at": "..." }/org/membersauth: JWT + OrgRoleGuard (any role in org)Roster of the caller's org. Returns 403 if caller isn't in any org.
[ { "id": "...", "user_id": "...", "email": "...", "role": "owner", "joined_at": "..." } ]/org/invitationsauth: JWT + admin/owner roleInvite by email. Sends invite email with one-time redemption token.
{ "email": "teammate@acme.com", "role": "operator" }{ "id": "...", "email": "...", "role": "operator", "expires_at": "..." }/org/invitations/acceptauth: JWT (invitee)Redeem an invitation token. Adds you to the org.
{ "token": "<raw invite token from email URL>" }{ "ok": true, "organization_id": "...", "role": "operator" }/org/auditauth: JWT + admin/owner roleAdmin audit log (member changes, vault-config changes, SCIM events, billing events).
event— filter by event sluglimit— 1-500 (default 100)before— ISO timestamp for keyset pagination
[ { "id": "...", "actor_kind": "user", "event": "member.invited", "target": "teammate@acme.com", "details": {...}, "created_at": "..." } ]/org/vault-configauth: JWT + any org roleStudio reads this on boot to learn where to mirror receipts.
{ "vault_config": { "driver": "s3", "bucket": "...", "region": "us-east-1", "access_key_ref": "env:AWS_ACCESS_KEY_ID", ... } }/org/exportauth: JWT + owner roleGDPR Art. 20 / CCPA export. JSON dump of everything the marketplace stores about the org.
{ "generated_at": "...", "organization": {...}, "members": [...], "invitations": [...], "org_listings": [...], "audit_log_recent": [...] }Billing
/billing/pricingauth: nonePublic pricing config for the /pricing + /org/billing pages. Everything here reads from marketplace env config.
{ "min_seats": 2, "trial_days": 14, "price_per_seat_month_usd_cents": 10000, "free_seat_cap": 1 }/org/billing/seat-statusauth: JWT + any org roleSeat gauge: tier, cap, active members, pending invites, subscription state.
{ "tier": "free", "seat_cap": 1, "seats_used": 1, "active_members": 1, "pending_invites": 0, "subscription": null }/org/billing/team/checkoutauth: JWT + owner roleStart a Team-tier Stripe Checkout session. Returns a hosted URL you redirect the buyer to. Card captured now, first charge at trial_end.
{ "seats": 5, "billing_email": "billing@acme.com" }{ "url": "https://checkout.stripe.com/c/pay/cs_live_..." }/org/billing/portalauth: JWT + admin/owner roleGet a Stripe Billing Portal deep-link for self-service subscription management.
{ "url": "https://billing.stripe.com/session/..." }Design partners
/design-partnersauth: noneSubmit a design-partner application. Rate-limited 3/hour/IP. Duplicates within 24h return silent success.
{
"full_name": "Jane Doe",
"work_email": "jane@acme.com",
"organization": "Acme Inc",
"role": "CTO",
"team_size": "11-50",
"regulated_data": "phi",
"target_regime": "HIPAA BAA",
"timeline": "60-90",
"what_would_change": "at least 20 characters describing the concrete change"
}{ "ok": true, "application_id": "...", "duplicate": false }Miscellaneous
/healthauth: noneLiveness probe. Returns 200 + { status: 'ok' } if the marketplace is up.
{ "status": "ok" }Anything under /admin/* is staff-only + not part of the public API contract. Anything returned only inside the JWT payload (session_generation, etc.) is implementation detail; don't build against it. If you need an integration surface that's not listed above, drop a note in our Discord with the use case — we'd rather add a real endpoint than have you scrape.
Error shape
Every 4xx/5xx returns:
{
"statusCode": 400,
"message": "human-readable error" or ["message 1", "message 2"],
"error": "Bad Request"
}Rate-limit errors are 429 with the same shape. Auth failures are 401 for missing/invalid JWT + 403 for insufficient role.