Testing your plugin
Install your plugin on a real store and exercise it end to end — before it is listed, reviewed, or seen by any merchant.
You do this with an install link: a one-time URL you generate for a plugin you own, which installs it on a store directly. It is not a shortcut around the platform. An install link runs the same install path as a marketplace install, so scope consent, tenant isolation and auditing all behave identically. What you test is what merchants will get.
What you need
Two things, and they are separate accounts because they do different jobs.
| Where | What it gives you | |
|---|---|---|
| Partner account | partner.myorbitcommerce.net | Your plugin: identity, scopes, versions, install links |
| A store | — | A real store context and the tokens your plugin calls the API with |
The partner dashboard does not create stores, and development stores are not self-serve yet — that is on our roadmap. In the meantime, if you are working with us on an integration, talk to your Orbit contact about arranging a store to develop against.
1. Make your plugin unlisted
Install links are the distribution channel for plugins that are not on the
public marketplace, so your plugin's visibility must be unlisted or private.
| Visibility | Who can install | Install route |
|---|---|---|
public | any merchant | marketplace listing |
unlisted | anyone with a link | install link |
private | one named store | install link bound to that store |
Creating a link for a public plugin returns 400 — public plugins are
installed from the listing, and a side-channel for them would only blur the
review story.
While you are building, unlisted is what you want. Switch to public and
submit for review when you are ready to be listed — see
Publishing.
2. Point the plugin at your local app
Your plugin is your own app, and the dashboard embeds it over HTTPS. Browsers
will not embed an insecure origin alongside a secure one, so http://localhost
will not load in the dashboard.
Run a tunnel — ngrok, Cloudflare Tunnel, or any HTTPS dev proxy — and set your
manifest's extensionPoints[].url to the tunnel URL:
{
"extensionPoints": [
{
"target": "dashboard.menu",
"url": "https://your-tunnel.ngrok.app/embed",
"label": "My Plugin"
}
]
}
Your app must also allow the embed:
Content-Security-Policy: frame-ancestors 'self'
https://*.orbitcommerce.net https://*.myorbitcommerce.net
3. Generate an install link
POST /api/plugins/mine/{pluginId}/install-links
Authorization: Bearer <partner JWT>
Content-Type: application/json
Every field is optional. An empty body gives you a reusable link that never expires.
| Field | Type | Effect |
|---|---|---|
expiresAt | ISO 8601 | Link stops working after this instant. Must be in the future |
maxUses | integer ≥ 1 | Cap on installs through this link. 1 makes it single-use |
targetStoreId | uuid | Binds the link to one store — only that store can resolve or install through it |
curl -X POST \
'https://api.myorbitcommerce.net/api/plugins/mine/my-plugin/install-links' \
--header 'Authorization: Bearer <partner JWT>' \
--header 'Content-Type: application/json' \
--data '{ "maxUses": 1, "expiresAt": "2026-12-31T23:59:59Z" }'
The response contains the raw token and the shareable URL. This is the only time either is returned — only a SHA-256 hash of the token is stored, so a database leak never exposes a usable link. If you lose it, revoke the link and generate another.
The URL looks like:
https://<vendor-dashboard>/plugins/install-link/<token>
4. Install it on the store
Open that URL while signed in to the store. You will see the same consent screen a merchant sees: your plugin's identity and the exact scopes it is asking for. Accept, and the plugin is installed and activated.
The granted scopes become your plugin's token scopes for that store. If you
later change oauth.scopes in your manifest, reinstall — consent is fixed at
install time, and an existing install keeps the scopes it was granted.
Under the hood the dashboard calls:
GET /api/plugins/install-links/{token} # consent summary
POST /api/plugins/install-links/{token}/install # install + activate
Both require x-store-id and a store session. You rarely call them directly —
the dashboard does it for you — but they are there if you are scripting a test
environment.
5. Exercise it
Your plugin now loads in the dashboard. The iframe handshake fires, the SDK receives the session token and store id, and your API calls are scoped to that store.
From here the loop is fast: edit locally, your tunnel serves the change, refresh the iframe. You only return to the partner dashboard when the manifest changes.
Calling the API outside the iframe
For Postman, curl, or the API reference, mint a session token for the store:
POST /api/plugins/{pluginId}/session-token
Authorization: Bearer <admin JWT for the store>
x-store-id: <store uuid>
That token is valid for one hour. For longer runs, exchange it:
POST /oauth/token/exchange
Authorization: Bearer <session token>
which returns an access token (one hour) plus a refresh token (90 days).
Refresh via POST /oauth/token/refresh. There is no long-lived access token —
see Authentication.
Managing links
GET /api/plugins/mine/{pluginId}/install-links # list, with a usable flag
DELETE /api/plugins/mine/{pluginId}/install-links/{linkId} # revoke
Revoking is idempotent. Revoke a link the moment it has served its purpose — anyone holding it can install your plugin on a store they control until it expires or hits its use cap.
Unknown, revoked, expired and exhausted links all return the same 404, so a
link that stops working gives no signal about which of those it was. A link
bound to a different store returns 403.
Troubleshooting
| Symptom | Cause |
|---|---|
400 creating a link | The plugin's visibility is public, or expiresAt is in the past |
403 creating a link | Your partner account does not own that plugin |
404 opening a link | Unknown, revoked, expired, or out of uses |
403 opening a link | The link is bound to a different store |
| Iframe stays blank | Your app is not reachable over HTTPS, or is missing the frame-ancestors header |
401 from /v1/* | Bad or expired token — or the install is missing that scope. Both return 401; check the granted scopes before assuming the token is at fault |
Next steps
- Publishing your plugin — submit for review and get listed
- Scopes and the Scope Catalog — pick the right permissions
- Authentication — the token model in full