JSI Distribute API Documentation
Everything you need to build against the JSI Distribute API — get your data, react to events in real time, and integrate from WordPress, a custom backend, or anywhere else that can make an HTTP request.
1. Quickstart
- Go to Developer Settings, create an API key, and pick the scopes you need.
- Copy the key immediately — it's shown once, right after creation, and never again. If you lose it, revoke it and create a new one.
- Call any endpoint below with
Authorization: Bearer <your_key>.
Base URL: https://staging.jsamindustry.com.ng/wp-json/jsi/v1
2. Authentication
Every request (except the Paystack webhook receiver, which isn't part of this API) needs a Bearer token — your raw API key — in the Authorization header:
Authorization: Bearer jsi_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Keys are scoped — a key only works against the endpoints covered by its assigned scope(s), even if the request is otherwise valid. Available scopes:
releases:readreleases:writeartists:readpackages:readorders:readroyalties:readwallet:readpayouts:readwebhooks:manage
A request with a valid key but the wrong scope gets 403 insufficient_scope, not a silent empty result — check the error code field, not just the status.
3. Response & error format
Every successful response is wrapped the same way:
{
"data": { ... or [ ... ] }
}
Every error uses WordPress's standard REST error shape:
{
"code": "insufficient_scope",
"message": "This key doesn't have the \"releases:write\" scope.",
"data": { "status": 403 }
}
Common error codes: unauthorized (401 — missing/invalid/revoked key), insufficient_scope (403), rate_limited (429), not_found (404).
Rate limit: 60 requests per minute, per API key. Every response counts toward the same key's rolling window regardless of which endpoint you call.
4. Endpoints
| Method | Path | Scope | What it does |
|---|---|---|---|
| GET | /releases | releases:read | List your releases |
| POST | /releases | releases:write | Create a release |
| GET | /releases/{id} | releases:read | Get one release |
| GET | /artists | artists:read | List your artist profiles |
| GET | /packages | packages:read | List available plans/packages |
| GET | /orders | orders:read | List your orders/payments |
| GET | /royalties | royalties:read | List your royalty records |
| GET | /wallet | wallet:read | Your wallet balance (via the Fintech Wallet bridge) |
| GET | /payouts | payouts:read | List your payout history |
| GET | /webhooks | webhooks:manage | List your webhook endpoints |
| POST | /webhooks | webhooks:manage | Register a new webhook endpoint |
| DELETE | /webhooks/{id} | webhooks:manage | Remove a webhook endpoint |
5. Example — cURL
curl "https://staging.jsamindustry.com.ng/wp-json/jsi/v1/releases" \ -H "Authorization: Bearer jsi_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
6. Building this into a WordPress site
If your integration also runs on WordPress (a separate site, a custom plugin, or a theme's functions.php), use wp_remote_get / wp_remote_post — never raw cURL — so redirects, SSL, and timeouts are handled the way WordPress core expects:
<?php
$response = wp_remote_get( 'https://staging.jsamindustry.com.ng/wp-json/jsi/v1/releases', [
'headers' => [
'Authorization' => 'Bearer ' . JSI_API_KEY, // store this in an option or constant, never hard-code it in a committed file
],
'timeout' => 15,
] );
if ( is_wp_error( $response ) ) {
error_log( 'JSI API error: ' . $response->get_error_message() );
} else {
$body = json_decode( wp_remote_retrieve_body( $response ), true );
$releases = $body['data'] ?? [];
// ...use $releases
}
Creating a release the same way:
<?php
$response = wp_remote_post( 'https://staging.jsamindustry.com.ng/wp-json/jsi/v1/releases', [
'headers' => [
'Authorization' => 'Bearer ' . JSI_API_KEY,
'Content-Type' => 'application/json',
],
'body' => wp_json_encode( [
'title' => 'My New Single',
'release_type' => 'single',
] ),
'timeout' => 15,
] );
Store the API key using update_option() (autoload off) or an environment-defined constant in wp-config.php — never in a file that ends up in version control.
7. Example — JavaScript / Node
const res = await fetch("https://staging.jsamindustry.com.ng/wp-json/jsi/v1/releases", {
headers: { Authorization: "Bearer " + process.env.JSI_API_KEY }
});
const { data: releases } = await res.json();
8. Webhooks
Instead of polling, register a webhook (from Developer Settings or via POST /webhooks) and JSI Distribute will POST a JSON payload to your target_url the moment one of these events happens:
release.createdrelease.submittedrelease.need_fixrelease.approvedrelease.rejectedrelease.liverelease.takedown_requestedrelease.taken_downpayment.pendingpayment.paidpayment.failedsubscription.createdsubscription.renewedsubscription.failedsubscription.expiredroyalty.importedpayout.requestedpayout.completed
Every delivery includes two headers:
X-JSI-Event: release.approved X-JSI-Signature: 5f2b1c... (HMAC-SHA256 of the raw request body, using your webhook's signing secret)
Always verify the signature before trusting a payload — anyone can POST to a public URL claiming to be JSI. Your webhook's signing secret is shown once, at creation, exactly like an API key.
Verifying in PHP:
<?php
$raw_body = file_get_contents( 'php://input' );
$signature = $_SERVER['HTTP_X_JSI_SIGNATURE'] ?? '';
$expected = hash_hmac( 'sha256', $raw_body, JSI_WEBHOOK_SECRET );
if ( ! hash_equals( $expected, $signature ) ) {
http_response_code( 401 );
exit;
}
$event = json_decode( $raw_body, true );
// handle $event['event'] / $event['data']
Verifying in Node/Express:
const crypto = require("crypto");
app.post("/webhooks/jsi", express.raw({ type: "*/*" }), (req, res) => {
const expected = crypto.createHmac("sha256", process.env.JSI_WEBHOOK_SECRET)
.update(req.body).digest("hex");
if (expected !== req.headers["x-jsi-signature"]) return res.sendStatus(401);
const event = JSON.parse(req.body);
// handle event.event / event.data
res.sendStatus(200);
});
Respond 2xx quickly (under a few seconds) — do slow work (sending emails, calling other APIs) after responding, in a queue or cron job, not inline in the webhook handler.
9. Plan requirements
API and webhook access requires a plan with API access enabled (currently the Developers plan, or any plan whose rules include API access). If your key stops working and you haven't revoked it, check that your subscription is still active on your dashboard.
10. Support
Something not covered here, or behaving unexpectedly? Reach out through your account dashboard's support channel with the request's approximate timestamp and (if relevant) the response body you received — that's normally enough for us to find it in wp_jsi_api_logs and tell you exactly what happened.