shiply.now

Publish with shiply (platform integration)

Embed a one-button "Publish with shiply" flow so your users publish into their own shiply account — and you earn 30% partner commission.

Platform Connect lets any tool — a page builder, an app generator, an agent IDE — embed a single "Publish with shiply" button. Clicking it sends the user through shiply's own signup/sign-in and consent screen, and hands your platform back an OAuth token scoped to only the sites it creates. The user owns the shiply account and pays shiply directly; you earn a recurring partner commission on every account you bring.

This is different from the MCP integration (that's for an agent acting on ITS OWN developer's shiply account) — Platform Connect is for a platform that wants to publish on behalf of its own end users, each into their own separate shiply account.

The one-line embed

<a href="https://shiply.now/connect/<slug>?response_type=code&redirect_uri=<your-callback>&code_challenge=<pkce-challenge>&code_challenge_method=S256&state=<state>">
  Publish with shiply
</a>

Replace <slug> with the slug shiply assigned your integration at approval (see "Getting registered" below), <your-callback> with your app's OAuth redirect URI (URL-encoded), <pkce-challenge> with a fresh RFC 7636 S256 code challenge, and <state> with an opaque value you'll verify on return. client_id and scope are NOT query params here — the connect flow resolves your registered client_id from <slug> and always grants the platform:sites scope.

Styled button

<a
  href="https://shiply.now/connect/<slug>?response_type=code&redirect_uri=<your-callback>&code_challenge=<pkce-challenge>&code_challenge_method=S256&state=<state>"
  style="display:inline-flex;align-items:center;gap:8px;padding:10px 18px;
         background:#111;color:#fff;border-radius:8px;font:600 14px system-ui;
         text-decoration:none;"
>
  Publish with shiply
</a>

For agents integrating this: the whole flow in one paragraph

Send the user to the /connect/<slug> URL above. They sign up or sign in to shiply (their own account, not yours) and approve a consent screen scoped to "create sites in your shiply account and manage the sites it creates — it cannot access your other sites, email, or billing." shiply redirects back to your redirect_uri with ?code=...&state=.... Exchange that code at POST /api/v1/oauth/token with your PKCE code_verifier (standard OAuth 2.1 authorization_code + PKCE — same endpoint the MCP OAuth flow uses). You get back an access_token (shp_at_…) and a 90-day rotating refresh_token. From then on, send Authorization: Bearer <access_token> on:

  • POST /api/v1/publish — create or update a site. A brand-new site is automatically stamped as "created by your platform"; updates to that same site keep working with the same token.
  • GET /api/v1/sites/<slug>/versions — deploy history for a site your token created.
  • GET /api/v1/me/entitlements — the connected user's plan (free / hobby / developer / studio) and feature flags, so you can gate your own UX (e.g. hide "add a custom domain" if they're on free) without guessing from a 403.

The token can only touch sites it created — a call against any other site in the user's account (or any account-level action: billing, email, other API keys) returns 403 { "error": { "code": "platform_scope_forbidden" } }. That's enforced server-side; there's nothing extra your integration needs to do to respect it.

Getting registered

The fastest path is the self-serve Apply for a client_id flow below — one POST, no email required. The steps here are the manual equivalent (useful if you're coordinating a custom requiredPlan or other detail directly):

  1. Call POST /api/v1/oauth/register (DCR — the same endpoint MCP clients use) with your redirect_uris to mint a client_id. No client_secret is issued or needed; auth is PKCE-only.
  2. Send Steve (stevejford1@gmail.com) your client_id, display name, logo URL, and (optionally) a minimum required plan — he registers the integration and approves it. Once approved, /connect/<slug> goes live.
  3. You earn 30% recurring partner commission on every account that converts to a paid plan through your button, for the standard commission window — the same partner program every shiply affiliate runs on.

Suspending an integration (e.g. for abuse) invalidates every outstanding token for it immediately — there's no stale-token window to worry about on either side.

Users can revoke you any time

Every connected user sees your integration at shiply.now/dashboard/connected-apps and can revoke it with one click. A revoked token stops working on its very next call; sites it already published stay live and stay the user's.

See also MCP clients and Authentication for the user's app if your platform separately needs end-user auth on the sites it publishes.

Apply for a client_id (self-serve, no shiply account needed)

You don't need to email anyone to get started — apply directly:

POST https://shiply.now/api/v1/integrate/apply
Content-Type: application/json

{
  "name": "<your platform name>",
  "slug": "<your-slug>",
  "redirect_uris": ["<your callback URL>"],
  "logoUrl": "<optional logo URL>",
  "use_case": "<optional: one line on what you're building>"
}

This is public and rate-limited (no auth required). It mints an OAuth client via Dynamic Client Registration under the hood, then creates a pending integration and alerts an admin — you don't need to call the DCR endpoint separately. The response is:

201 { "client_id": "mcpc_...", "slug": "<your-slug>", "status": "pending" }

slug must be 3-32 chars: lowercase letters, numbers, hyphens (not leading/trailing) — it becomes your public shiply.now/connect/<slug> path. A taken slug returns 409; pick another and retry. An admin reviews by hand at /admin/platforms — you'll get an email on approval, and only then does /connect/<slug> start resolving. Nothing above grants any privilege by itself: the pending row can't authorize a token until it's approved.

If you'd rather skip the form, /integrate has a self-contained copy-paste brief for your coding agent that covers this whole flow — apply, embed, exchange, publish — in one block.

Embed the button

Once approved, drop the hosted button asset instead of building your own — it's the exact button shiply's own docs and /integrate page use, matched to shiply's brand mark and colors, and cached so it loads instantly:

<a href="https://shiply.now/connect/<slug>?response_type=code&redirect_uri=<your-callback>&code_challenge=<pkce-challenge>&code_challenge_method=S256&state=<state>">
  <img
    src="https://shiply.now/button/publish-with-shiply.svg"
    alt="Publish with shiply"
    height="40"
  />
</a>

GET /button/publish-with-shiply.svg always returns 200, content-type: image/svg+xml. Two query params customize it, and can be combined:

  • ?theme=dark — dark fill for dark-mode UIs (default is light).
  • ?label=integrate — swaps the copy to "Integrate with shiply" (default "Publish with shiply").

As always, build the href server-side with a fresh PKCE code_challenge and state per render — the <img> src never changes, only the link around it does.

Domain packs (for your users on a paid plan)

Each shiply plan ships with a base custom-domain allowance. If a connected user needs more than their plan's base cap, they can add +25-domain packs at $40/mo (or $400/yr on an annual plan) from their own shiply.now/dashboard/money/plan page — this is a plan add-on the user buys directly from shiply, not something your integration needs to expose or bill for. Packs aren't available on the Free plan. Nothing on your side changes: your token's custom-domain calls just start succeeding again once the user's effective cap (plan base + packs × 25) covers their count.

The create-or-connect flow, from the user's side

/connect/<slug> is the same screen whether the person clicking your button already has a shiply account or not — there's no separate "new user" vs. "existing user" link for you to choose between:

  • No shiply account yet → they sign up on the spot (email or OAuth), land straight on the consent screen, and approve. One flow, no detour through a generic signup page first.
  • Already have a shiply account → they sign in and go straight to the same consent screen.

Either way you get the same ?code=...&state=... redirect back to your redirect_uri, and the same token-exchange call at POST /api/v1/oauth/token — your callback code doesn't need to know or care which path the user took.