Skip to content

API Documentation

Use the Sagging Royalties API to programmatically interact with your data. This is useful for automating the upload of sales reports from your own scripts or systems.

Overview

  • Base URL: https://saggingroyals.com/api
  • Authentication: All requests must include your API Key in the X-API-Key header.
  • Full Reference: Interactive API Documentation

Authentication

Obtain your API Key from your user profile page (https://saggingroyals.com/profile/). Copy it immediately into your password manager or other secure location, as it will only be shown once!

Header: X-API-Key: <YOUR_API_KEY>

Sales Channels

List Channel Types

Retrieve a list of global sales channel types supported by the system (e.g., INGRAM, KDP).

GET /channel-types

Request

saggingroyals channels types
curl -H "X-API-Key: <YOUR_API_KEY>" \
     https://saggingroyals.com/api/channel-types
import requests

headers = {"X-API-Key": "<YOUR_API_KEY>"}
response = requests.get("https://saggingroyals.com/api/channel-types", headers=headers)
print(response.json())
fetch("https://saggingroyals.com/api/channel-types", {
  headers: { "X-API-Key": "<YOUR_API_KEY>" }
})
.then(response => response.json())
.then(data => console.log(data));

Response

[
  {
    "name": "Ingram",
    "slug": "ingram",
    "code": "INGRAM",
    "description": "Ingram Content Group"
  },
  {
    "name": "KDP",
    "slug": "kdp",
    "code": "KDP",
    "description": "Kindle Direct Publishing"
  }
]

List Channels

Retrieve a list of configured sales channels for the publisher.

GET /publishers/{publisher_slug}/channels

Request

saggingroyals channels list your-slug
curl -H "X-API-Key: <YOUR_API_KEY>" \
     https://saggingroyals.com/api/publishers/your-slug/channels
import requests

headers = {"X-API-Key": "<YOUR_API_KEY>"}
response = requests.get(
    "https://saggingroyals.com/api/publishers/your-slug/channels",
    headers=headers
)
print(response.json())
fetch("https://saggingroyals.com/api/publishers/your-slug/channels", {
  headers: { "X-API-Key": "<YOUR_API_KEY>" }
})
.then(response => response.json())
.then(data => console.log(data));

Response

[
  {
    "name": "Ingram",
    "slug": "ingram",
    "type_code": "INGRAM",
    "type_name": "Ingram",
    "update_cadence": "MONTHLY",
    "is_visible": true
  }
]

Create Sales Channel

Configure a new sales channel.

POST /publishers/{publisher_slug}/channels

Request JSON Body: {"type_code": "INGRAM", "name": "Ingram", "slug": "ingram", "update_cadence": "monthly"}

saggingroyals channels create your-slug --name "Ingram" --slug ingram --type INGRAM
curl -X POST "https://saggingroyals.com/api/publishers/your-slug/channels" \
     -H "X-API-Key: <YOUR_API_KEY>" \
     -H "Content-Type: application/json" \
     -d '{"type_code": "INGRAM", "name": "Ingram", "slug": "ingram"}'
import requests

url = "https://saggingroyals.com/api/publishers/your-slug/channels"
headers = {"X-API-Key": "<YOUR_API_KEY>"}
data = {
    "type_code": "INGRAM", 
    "name": "Ingram", 
    "slug": "ingram"
}

response = requests.post(url, headers=headers, json=data)
print(response.json())
fetch("https://saggingroyals.com/api/publishers/your-slug/channels", {
  method: "POST",
  headers: { 
    "X-API-Key": "<YOUR_API_KEY>",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    type_code: "INGRAM", 
    name: "Ingram", 
    slug: "ingram"
  })
})
.then(response => response.json())
.then(data => console.log(data));

Response

{
  "name": "Ingram",
  "slug": "ingram",
  "type_code": "INGRAM",
  "type_name": "Ingram",
  "update_cadence": "MONTHLY",
  "is_visible": true
}

Catalog & Projects

List Projects

List all projects for a publisher.

GET /publishers/{publisher_slug}/projects

Request

saggingroyals catalog list your-slug
curl -H "X-API-Key: <YOUR_API_KEY>" \
     https://saggingroyals.com/api/publishers/your-slug/projects
import requests

headers = {"X-API-Key": "<YOUR_API_KEY>"}
response = requests.get(
    "https://saggingroyals.com/api/publishers/your-slug/projects", 
    headers=headers
)
print(response.json())
fetch("https://saggingroyals.com/api/publishers/your-slug/projects", {
  headers: { "X-API-Key": "<YOUR_API_KEY>" }
})
.then(response => response.json())
.then(data => console.log(data));

Response

[
  {
    "title": "My Book",
    "slug": "my-book",
    "publication_date": "2025-01-01",
    "authors": ["John Doe"],
    "publications": []
  }
]

Create Project

Create a new project.

POST /publishers/{publisher_slug}/projects

Request JSON Body: {"title": "My Book", "slug": "my-book", "author": "John Doe", "publication_date": "2025-01-01"}

saggingroyals projects create your-slug "My Book" --slug my-book --author "John Doe"
curl -X POST "https://saggingroyals.com/api/publishers/your-slug/projects" \
     -H "X-API-Key: <YOUR_API_KEY>" \
     -H "Content-Type: application/json" \
     -d '{"title": "My Book", "slug": "my-book", "author": "John Doe"}'
import requests

url = "https://saggingroyals.com/api/publishers/your-slug/projects"
headers = {"X-API-Key": "<YOUR_API_KEY>"}
data = {"title": "My Book", "slug": "my-book", "author": "John Doe"}

response = requests.post(url, headers=headers, json=data)
print(response.json())
fetch("https://saggingroyals.com/api/publishers/your-slug/projects", {
  method: "POST",
  headers: { 
    "X-API-Key": "<YOUR_API_KEY>",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    title: "My Book", 
    slug: "my-book", 
    author: "John Doe"
  })
})
.then(response => response.json())
.then(data => console.log(data));

Response

{
  "title": "My Book",
  "slug": "my-book",
  "publication_date": "2025-01-01",
  "authors": ["John Doe"],
  "publications": []
}

Get Project Details

Retrieve metadata for a specific project.

GET /publishers/{publisher_slug}/projects/{project_slug}

Request

saggingroyals projects get your-slug the-great-gatsby
curl -H "X-API-Key: <YOUR_API_KEY>" \
     https://saggingroyals.com/api/publishers/your-slug/projects/the-great-gatsby
import requests

headers = {"X-API-Key": "<YOUR_API_KEY>"}
response = requests.get(
    "https://saggingroyals.com/api/publishers/your-slug/projects/the-great-gatsby",
    headers=headers
)
print(response.json())
fetch("https://saggingroyals.com/api/publishers/your-slug/projects/the-great-gatsby", {
  headers: { "X-API-Key": "<YOUR_API_KEY>" }
})
.then(response => response.json())
.then(data => console.log(data));

Response

{
  "title": "The Great Gatsby",
  "slug": "the-great-gatsby",
  "publication_date": "1925-04-10",
  "authors": ["F. Scott Fitzgerald"],
  "publications": [
    {
      "isbn": "978-0743273565",
      "format": "Paperback",
      "us_list_price": 17.00
    }
  ]
}

Create Publication (ISBN)

Add a publication (ISBN) to a project.

POST /publishers/{publisher_slug}/projects/{project_slug}/publications

Request JSON Body: {"isbn": "978-1234567890", "format": "Paperback", "price": 19.99, "sku": "SKU123"}

saggingroyals projects create-publication your-slug my-book --isbn 978-1234567890 --format Paperback --price 19.99
curl -X POST "https://saggingroyals.com/api/publishers/your-slug/projects/my-book/publications" \
     -H "X-API-Key: <YOUR_API_KEY>" \
     -H "Content-Type: application/json" \
     -d '{"isbn": "978-1234567890", "format": "Paperback", "price": 19.99}'
import requests

url = "https://saggingroyals.com/api/publishers/your-slug/projects/my-book/publications"
headers = {"X-API-Key": "<YOUR_API_KEY>"}
data = {
    "isbn": "978-1234567890", 
    "format": "Paperback", 
    "price": 19.99
}

response = requests.post(url, headers=headers, json=data)
print(response.json())
fetch("https://saggingroyals.com/api/publishers/your-slug/projects/my-book/publications", {
  method: "POST",
  headers: { 
    "X-API-Key": "<YOUR_API_KEY>",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    isbn: "978-1234567890", 
    format: "Paperback", 
    price: 19.99
  })
})
.then(response => response.json())
.then(data => console.log(data));

Response

{
  "isbn": "978-1234567890",
  "format": "Paperback",
  "us_list_price": 19.99
}

Export Catalog

Download the catalog as a CSV file.

GET /publishers/{publisher_slug}/catalog/export

Request

saggingroyals catalog export your-slug --out catalog.csv
curl -H "X-API-Key: <YOUR_API_KEY>" \
     -o catalog.csv \
     https://saggingroyals.com/api/publishers/your-slug/catalog/export
import requests

headers = {"X-API-Key": "<YOUR_API_KEY>"}
response = requests.get(
    "https://saggingroyals.com/api/publishers/your-slug/catalog/export",
    headers=headers
)
with open("catalog.csv", "wb") as f:
    f.write(response.content)
// Node.js example (using fetch + fs)
const fs = require('fs');

fetch("https://saggingroyals.com/api/publishers/your-slug/catalog/export", {
  headers: { "X-API-Key": "<YOUR_API_KEY>" }
})
.then(res => res.arrayBuffer())
.then(buffer => fs.writeFileSync("catalog.csv", Buffer.from(buffer)));

Response The response is a downloadable CSV file.

Sales Data

Upload Sales Report

To upload a CSV or Excel sales report for processing:

POST /publishers/{publisher_slug}/upload-sales-report

Request

saggingroyals upload-sales your-slug Ingram ./2025-10-IsdSales.csv
curl -X POST "https://saggingroyals.com/api/publishers/your-slug/upload-sales-report" \
  -H "X-API-Key: your-api-key" \
  -F "channel=Ingram" \
  -F "file=@./2025-10-IsdSales.csv"
import requests

url = "https://saggingroyals.com/api/publishers/your-slug/upload-sales-report"
headers = {"X-API-Key": "<YOUR_API_KEY>"}
files = {"file": open("2025-10-IsdSales.csv", "rb")}
data = {"channel": "Ingram"}

response = requests.post(url, headers=headers, data=data, files=files)
print(response.json())
// Browser example (Form Data)
const formData = new FormData();
formData.append("channel", "Ingram");
formData.append("file", fileInput.files[0]);

fetch("https://saggingroyals.com/api/publishers/your-slug/upload-sales-report", {
  method: "POST",
  headers: { "X-API-Key": "<YOUR_API_KEY>" },
  body: formData
})
.then(response => response.json())
.then(data => console.log(data));

Response

{
  "status": "success",
  "file_id": 1234,
  "filename": "2025-10-IsdSales.csv",
  "message": "File uploaded successfully"
}

Get Sales Ledger

Retrieve raw sales records.

GET /publishers/{publisher_slug}/sales/ledger

Parameters

  • start_date (query, date)
  • end_date (query, date)
  • channel (query, string): Channel slug or code

Request

saggingroyals sales ledger your-slug --start 2025-01-01
curl -H "X-API-Key: <YOUR_API_KEY>" \
     "https://saggingroyals.com/api/publishers/your-slug/sales/ledger?start_date=2025-01-01"
import requests

headers = {"X-API-Key": "<YOUR_API_KEY>"}
params = {"start_date": "2025-01-01"}
response = requests.get(
    "https://saggingroyals.com/api/publishers/your-slug/sales/ledger",
    headers=headers,
    params=params
)
print(response.json())
const params = new URLSearchParams({ start_date: "2025-01-01" });
fetch(`https://saggingroyals.com/api/publishers/your-slug/sales/ledger?${params}`, {
  headers: { "X-API-Key": "<YOUR_API_KEY>" }
})
.then(response => response.json())
.then(data => console.log(data));

Response

[
  {
    "id": 1,
    "publication_isbn": "978-1234567890",
    "publication_title": "My Book",
    "channel": "Ingram",
    "period_end": "2025-01-31",
    "quantity": 10,
    "net_receipts_usd": 150.00
  }
]

Royalties

List Royalty Holders

GET /publishers/{publisher_slug}/royalty-holders

Request

saggingroyals royalties holders list your-slug
curl -H "X-API-Key: <YOUR_API_KEY>" \
     https://saggingroyals.com/api/publishers/your-slug/royalty-holders
import requests

headers = {"X-API-Key": "<YOUR_API_KEY>"}
response = requests.get(
    "https://saggingroyals.com/api/publishers/your-slug/royalty-holders",
    headers=headers
)
print(response.json())
fetch("https://saggingroyals.com/api/publishers/your-slug/royalty-holders", {
  headers: { "X-API-Key": "<YOUR_API_KEY>" }
})
.then(response => response.json())
.then(data => console.log(data));

Response

[
  {
    "id": 1,
    "name": "Jane Author",
    "email": "jane@example.com",
    "address_line1": "123 Main St",
    "city": "New York",
    "state_province": "NY",
    "postal_code": "10001",
    "country": "USA"
  }
]

Create Royalty Holder

POST /publishers/{publisher_slug}/royalty-holders

Request JSON Body: {"name": "Jane Author", "email": "jane@example.com"}

saggingroyals royalties holders create your-slug --name "Jane Author" --email "jane@example.com"
curl -X POST "https://saggingroyals.com/api/publishers/your-slug/royalty-holders" \
     -H "X-API-Key: <YOUR_API_KEY>" \
     -H "Content-Type: application/json" \
     -d '{"name": "Jane Author", "email": "jane@example.com"}'
import requests

url = "https://saggingroyals.com/api/publishers/your-slug/royalty-holders"
headers = {"X-API-Key": "<YOUR_API_KEY>"}
data = {"name": "Jane Author", "email": "jane@example.com"}

response = requests.post(url, headers=headers, json=data)
print(response.json())
fetch("https://saggingroyals.com/api/publishers/your-slug/royalty-holders", {
  method: "POST",
  headers: { 
    "X-API-Key": "<YOUR_API_KEY>",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    name: "Jane Author", 
    email: "jane@example.com"
  })
})
.then(response => response.json())
.then(data => console.log(data));

Response

{
  "id": 2,
  "name": "Jane Author",
  "email": "jane@example.com",
  "address_line1": null,
  "city": null,
  "state_province": null,
  "postal_code": null,
  "country": null
}

List Royalty Payments

GET /publishers/{publisher_slug}/royalty-payments

Request

saggingroyals royalties payments list your-slug
curl -H "X-API-Key: <YOUR_API_KEY>" \
     https://saggingroyals.com/api/publishers/your-slug/royalty-payments
import requests

headers = {"X-API-Key": "<YOUR_API_KEY>"}
response = requests.get(
    "https://saggingroyals.com/api/publishers/your-slug/royalty-payments",
    headers=headers
)
print(response.json())
fetch("https://saggingroyals.com/api/publishers/your-slug/royalty-payments", {
  headers: { "X-API-Key": "<YOUR_API_KEY>" }
})
.then(response => response.json())
.then(data => console.log(data));

Response

[
  {
    "id": 101,
    "holder_id": 1,
    "holder_name": "Jane Author",
    "amount": 500.00,
    "currency": "USD",
    "payment_date": "2025-01-15",
    "notes": "Check #123"
  }
]

Record Royalty Payment

Record an outgoing payment to a royalty holder.

POST /publishers/{publisher_slug}/royalty-payments

Request JSON Body: {"holder_id": 1, "amount": 500.00, "date": "2025-01-15", "notes": "Check #123"}

saggingroyals royalties payments create your-slug --holder-id 1 --amount 500.00 --date 2025-01-15
curl -X POST "https://saggingroyals.com/api/publishers/your-slug/royalty-payments" \
     -H "X-API-Key: <YOUR_API_KEY>" \
     -H "Content-Type: application/json" \
     -d '{"holder_id": 1, "amount": 500.00, "date": "2025-01-15"}'
import requests

url = "https://saggingroyals.com/api/publishers/your-slug/royalty-payments"
headers = {"X-API-Key": "<YOUR_API_KEY>"}
data = {"holder_id": 1, "amount": 500.00, "date": "2025-01-15"}

response = requests.post(url, headers=headers, json=data)
print(response.json())
fetch("https://saggingroyals.com/api/publishers/your-slug/royalty-payments", {
  method: "POST",
  headers: { 
    "X-API-Key": "<YOUR_API_KEY>",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    holder_id: 1, 
    amount: 500.00, 
    date: "2025-01-15"
  })
})
.then(response => response.json())
.then(data => console.log(data));

Response

{
  "id": 102,
  "holder_id": 1,
  "holder_name": "Jane Author",
  "amount": 500.00,
  "currency": "USD",
  "payment_date": "2025-01-15",
  "notes": null
}

List Royalty Statements

Retrieve a list of generated royalty statements.

GET /publishers/{publisher_slug}/statements

Request

saggingroyals royalties statements list your-slug
curl -H "X-API-Key: <YOUR_API_KEY>" \
     https://saggingroyals.com/api/publishers/your-slug/statements
import requests

headers = {"X-API-Key": "<YOUR_API_KEY>"}
response = requests.get(
    "https://saggingroyals.com/api/publishers/your-slug/statements",
    headers=headers
)
print(response.json())
fetch("https://saggingroyals.com/api/publishers/your-slug/statements", {
  headers: { "X-API-Key": "<YOUR_API_KEY>" }
})
.then(response => response.json())
.then(data => console.log(data));

Response

[
  {
    "id": 55,
    "holder_name": "Jane Author",
    "period_start": "2024-07-01",
    "period_end": "2024-12-31",
    "opening_balance": 0.00,
    "earnings": 1250.00,
    "payments": 500.00,
    "closing_balance": 750.00,
    "is_locked": true
  }
]

Generate Statements

Trigger the generation of royalty statements for a period.

POST /publishers/{publisher_slug}/statements/generate

Request JSON Body: {"period_start": "2024-07-01", "period_end": "2024-12-31"}

saggingroyals royalties statements generate your-slug --start 2024-07-01 --end 2024-12-31
curl -X POST "https://saggingroyals.com/api/publishers/your-slug/statements/generate" \
     -H "X-API-Key: <YOUR_API_KEY>" \
     -H "Content-Type: application/json" \
     -d '{"period_start": "2024-07-01", "period_end": "2024-12-31"}'
import requests

url = "https://saggingroyals.com/api/publishers/your-slug/statements/generate"
headers = {"X-API-Key": "<YOUR_API_KEY>"}
data = {"period_start": "2024-07-01", "period_end": "2024-12-31"}

response = requests.post(url, headers=headers, json=data)
print(response.json())
fetch("https://saggingroyals.com/api/publishers/your-slug/statements/generate", {
  method: "POST",
  headers: { 
    "X-API-Key": "<YOUR_API_KEY>",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    period_start: "2024-07-01", 
    period_end: "2024-12-31"
  })
})
.then(response => response.json())
.then(data => console.log(data));

Response

{
  "status": "success",
  "message": "Statement generation queued for 12 holders",
  "generated_count": 12
}

Download Statement PDF

Download the PDF file for a specific statement.

GET /publishers/{publisher_slug}/statements/{statement_id}/download

Request

saggingroyals royalties statements download your-slug 123 --out statement.pdf
curl -H "X-API-Key: <YOUR_API_KEY>" \
     -O -J \
     https://saggingroyals.com/api/publishers/your-slug/statements/123/download
import requests

headers = {"X-API-Key": "<YOUR_API_KEY>"}
response = requests.get(
    "https://saggingroyals.com/api/publishers/your-slug/statements/123/download",
    headers=headers
)
with open("statement.pdf", "wb") as f:
    f.write(response.content)
// Node.js example
const fs = require('fs');
fetch("https://saggingroyals.com/api/publishers/your-slug/statements/123/download", {
  headers: { "X-API-Key": "<YOUR_API_KEY>" }
})
.then(res => res.arrayBuffer())
.then(buffer => fs.writeFileSync("statement.pdf", Buffer.from(buffer)));

Response The response is a downloadable PDF file.

Incoming Payments

Track payments received from sales channels (distributor payouts).

List Payment Ledger

GET /publishers/{publisher_slug}/payments/ledger

Parameters

  • channel (query, string): Filter by channel slug
  • date_start (query, date): Filter by start date
  • date_end (query, date): Filter by end date

Request

saggingroyals payments list your-slug --channel ingram
curl -H "X-API-Key: <YOUR_API_KEY>" \
     "https://saggingroyals.com/api/publishers/your-slug/payments/ledger?channel=ingram"
import requests

headers = {"X-API-Key": "<YOUR_API_KEY>"}
params = {"channel": "ingram"}
response = requests.get(
    "https://saggingroyals.com/api/publishers/your-slug/payments/ledger",
    headers=headers,
    params=params
)
print(response.json())
const params = new URLSearchParams({ channel: "ingram" });
fetch(`https://saggingroyals.com/api/publishers/your-slug/payments/ledger?${params}`, {
  headers: { "X-API-Key": "<YOUR_API_KEY>" }
})
.then(response => response.json())
.then(data => console.log(data));

Response

[
  {
    "id": 1,
    "channel": "ingram",
    "period_end": "2025-03-31",
    "payout_amount": 1234.56,
    "payout_currency": "USD"
  }
]

Upload Payment Report

Upload a payment report file from a sales channel.

POST /publishers/{publisher_slug}/payments/reports

Request

saggingroyals payments upload your-slug ingram ./ingram-payment-2025-03.csv
curl -X POST "https://saggingroyals.com/api/publishers/your-slug/payments/reports" \
     -H "X-API-Key: <YOUR_API_KEY>" \
     -F "channel=ingram" \
     -F "file=@./ingram-payment-2025-03.csv"
import requests

url = "https://saggingroyals.com/api/publishers/your-slug/payments/reports"
headers = {"X-API-Key": "<YOUR_API_KEY>"}
files = {"file": open("ingram-payment-2025-03.csv", "rb")}
data = {"channel": "ingram"}

response = requests.post(url, headers=headers, data=data, files=files)
print(response.json())
const formData = new FormData();
formData.append("channel", "ingram");
formData.append("file", fileInput.files[0]);

fetch("https://saggingroyals.com/api/publishers/your-slug/payments/reports", {
  method: "POST",
  headers: { "X-API-Key": "<YOUR_API_KEY>" },
  body: formData
})
.then(response => response.json())
.then(data => console.log(data));

Record Payment Manually

Manually record a payment received from a channel.

POST /publishers/{publisher_slug}/payments/records

Request JSON Body: {"channel": "ingram", "payout_amount": 1234.56, "payout_currency": "USD", "period_end": "2025-03-31"}

saggingroyals payments add your-slug --channel ingram --amount 1234.56 --date 2025-03-31
curl -X POST "https://saggingroyals.com/api/publishers/your-slug/payments/records" \
     -H "X-API-Key: <YOUR_API_KEY>" \
     -H "Content-Type: application/json" \
     -d '{"channel": "ingram", "payout_amount": 1234.56, "payout_currency": "USD", "period_end": "2025-03-31"}'
import requests

url = "https://saggingroyals.com/api/publishers/your-slug/payments/records"
headers = {"X-API-Key": "<YOUR_API_KEY>"}
data = {
    "channel": "ingram",
    "payout_amount": 1234.56,
    "payout_currency": "USD",
    "period_end": "2025-03-31"
}

response = requests.post(url, headers=headers, json=data)
print(response.json())
fetch("https://saggingroyals.com/api/publishers/your-slug/payments/records", {
  method: "POST",
  headers: {
    "X-API-Key": "<YOUR_API_KEY>",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    channel: "ingram",
    payout_amount: 1234.56,
    payout_currency: "USD",
    period_end: "2025-03-31"
  })
})
.then(response => response.json())
.then(data => console.log(data));

Expenses

Track project costs that reduce the royalty pool for net-receipts projects. See Expense Tracking for a detailed explanation.

List Expenses

GET /publishers/{publisher_slug}/expenses

Parameters

  • project_slug (query, string): Filter by project
  • category (query, string): Filter by category (production, editorial, marketing, distribution_fulfillment, other)

Request

saggingroyals expenses list your-slug --project my-book
curl -H "X-API-Key: <YOUR_API_KEY>" \
     "https://saggingroyals.com/api/publishers/your-slug/expenses?project_slug=my-book"
import requests

headers = {"X-API-Key": "<YOUR_API_KEY>"}
params = {"project_slug": "my-book"}
response = requests.get(
    "https://saggingroyals.com/api/publishers/your-slug/expenses",
    headers=headers,
    params=params
)
print(response.json())
const params = new URLSearchParams({ project_slug: "my-book" });
fetch(`https://saggingroyals.com/api/publishers/your-slug/expenses?${params}`, {
  headers: { "X-API-Key": "<YOUR_API_KEY>" }
})
.then(response => response.json())
.then(data => console.log(data));

Response

[
  {
    "id": 1,
    "project_slug": "my-book",
    "project_title": "My Book",
    "description": "Cover design",
    "category": "production",
    "amount": 1500.00,
    "quantity": null,
    "unit_cost": null,
    "effective_date": "2025-03-15",
    "sales_channel_name": null,
    "notes": ""
  }
]

Create Expense

POST /publishers/{publisher_slug}/expenses

Request JSON Body:

{
  "project_slug": "my-book",
  "description": "Cover design",
  "category": "production",
  "amount": 1500.00,
  "effective_date": "2025-03-15",
  "quantity": null,
  "sales_channel_id": null,
  "notes": "Paid to designer Jane Smith"
}
saggingroyals expenses create your-slug --project my-book \
    --description "Cover design" --category production \
    --amount 1500.00 --date 2025-03-15 \
    --notes "Paid to designer Jane Smith"
curl -X POST "https://saggingroyals.com/api/publishers/your-slug/expenses" \
     -H "X-API-Key: <YOUR_API_KEY>" \
     -H "Content-Type: application/json" \
     -d '{"project_slug": "my-book", "description": "Cover design", "category": "production", "amount": 1500.00, "effective_date": "2025-03-15"}'
import requests

url = "https://saggingroyals.com/api/publishers/your-slug/expenses"
headers = {"X-API-Key": "<YOUR_API_KEY>"}
data = {
    "project_slug": "my-book",
    "description": "Cover design",
    "category": "production",
    "amount": 1500.00,
    "effective_date": "2025-03-15"
}

response = requests.post(url, headers=headers, json=data)
print(response.json())
fetch("https://saggingroyals.com/api/publishers/your-slug/expenses", {
  method: "POST",
  headers: {
    "X-API-Key": "<YOUR_API_KEY>",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    project_slug: "my-book",
    description: "Cover design",
    category: "production",
    amount: 1500.00,
    effective_date: "2025-03-15"
  })
})
.then(response => response.json())
.then(data => console.log(data));

Response

{
  "id": 1,
  "project_slug": "my-book",
  "project_title": "My Book",
  "description": "Cover design",
  "category": "production",
  "amount": 1500.00,
  "quantity": null,
  "unit_cost": null,
  "effective_date": "2025-03-15",
  "sales_channel_name": null,
  "notes": ""
}

Get Expense

GET /publishers/{publisher_slug}/expenses/{expense_id}

Request

saggingroyals expenses get your-slug 1
curl -H "X-API-Key: <YOUR_API_KEY>" \
     https://saggingroyals.com/api/publishers/your-slug/expenses/1
import requests

headers = {"X-API-Key": "<YOUR_API_KEY>"}
response = requests.get(
    "https://saggingroyals.com/api/publishers/your-slug/expenses/1",
    headers=headers
)
print(response.json())
fetch("https://saggingroyals.com/api/publishers/your-slug/expenses/1", {
  headers: { "X-API-Key": "<YOUR_API_KEY>" }
})
.then(response => response.json())
.then(data => console.log(data));

Response

Same format as the Create Expense response.

Update Expense

PATCH /publishers/{publisher_slug}/expenses/{expense_id}

Only include fields you want to change.

Request JSON Body: {"amount": 1750.00, "notes": "Revised invoice"}

saggingroyals expenses update your-slug 1 --amount 1750.00 --notes "Revised invoice"
curl -X PATCH "https://saggingroyals.com/api/publishers/your-slug/expenses/1" \
     -H "X-API-Key: <YOUR_API_KEY>" \
     -H "Content-Type: application/json" \
     -d '{"amount": 1750.00, "notes": "Revised invoice"}'
import requests

url = "https://saggingroyals.com/api/publishers/your-slug/expenses/1"
headers = {"X-API-Key": "<YOUR_API_KEY>"}
data = {"amount": 1750.00, "notes": "Revised invoice"}

response = requests.patch(url, headers=headers, json=data)
print(response.json())
fetch("https://saggingroyals.com/api/publishers/your-slug/expenses/1", {
  method: "PATCH",
  headers: {
    "X-API-Key": "<YOUR_API_KEY>",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    amount: 1750.00,
    notes: "Revised invoice"
  })
})
.then(response => response.json())
.then(data => console.log(data));

Response

Same format as the Create Expense response, with updated fields.

Delete Expense

DELETE /publishers/{publisher_slug}/expenses/{expense_id}

Request

saggingroyals expenses delete your-slug 1
curl -X DELETE "https://saggingroyals.com/api/publishers/your-slug/expenses/1" \
     -H "X-API-Key: <YOUR_API_KEY>"
import requests

url = "https://saggingroyals.com/api/publishers/your-slug/expenses/1"
headers = {"X-API-Key": "<YOUR_API_KEY>"}

response = requests.delete(url, headers=headers)
print(response.json())
fetch("https://saggingroyals.com/api/publishers/your-slug/expenses/1", {
  method: "DELETE",
  headers: { "X-API-Key": "<YOUR_API_KEY>" }
})
.then(response => response.json())
.then(data => console.log(data));

Response

{
  "success": true,
  "message": "Expense deleted."
}

Users

List Publisher Users

List users with access to the publisher account.

GET /publishers/{publisher_slug}/users

Request

saggingroyals users list your-slug
curl -H "X-API-Key: <YOUR_API_KEY>" \
     https://saggingroyals.com/api/publishers/your-slug/users
import requests

headers = {"X-API-Key": "<YOUR_API_KEY>"}
response = requests.get(
    "https://saggingroyals.com/api/publishers/your-slug/users",
    headers=headers
)
print(response.json())
fetch("https://saggingroyals.com/api/publishers/your-slug/users", {
  headers: { "X-API-Key": "<YOUR_API_KEY>" }
})
.then(response => response.json())
.then(data => console.log(data));

Response

[
  {
    "id": 1,
    "email": "owner@example.com",
    "role": "OWNER",
    "first_name": "Publisher",
    "last_name": "Owner"
  },
  {
    "id": 2,
    "email": "editor@example.com",
    "role": "EDITOR",
    "first_name": "Editor",
    "last_name": "User"
  }
]

Invite User

Invite a new user to the publisher account.

POST /publishers/{publisher_slug}/invitations

Request JSON Body: {"email": "editor@example.com", "role": "editor"}

saggingroyals users invite your-slug editor@example.com --role editor
curl -X POST "https://saggingroyals.com/api/publishers/your-slug/invitations" \
     -H "X-API-Key: <YOUR_API_KEY>" \
     -H "Content-Type: application/json" \
     -d '{"email": "editor@example.com", "role": "editor"}'
import requests

url = "https://saggingroyals.com/api/publishers/your-slug/invitations"
headers = {"X-API-Key": "<YOUR_API_KEY>"}
data = {"email": "editor@example.com", "role": "editor"}

response = requests.post(url, headers=headers, json=data)
print(response.json())
fetch("https://saggingroyals.com/api/publishers/your-slug/invitations", {
  method: "POST",
  headers: { 
    "X-API-Key": "<YOUR_API_KEY>",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    email: "editor@example.com", 
    role: "editor"
  })
})
.then(response => response.json())
.then(data => console.log(data));

Response

{
  "status": "success",
  "message": "Invitation sent to editor@example.com"
}