The credits API
This is the one endpoint you build. The toll calls it to learn who wrote an article and where their share goes, which also decides whether the article is tolled at all. It is read-only, it holds no money, and it never sees a payment.
The request
GET https://your-site.example/api/credits/<slug><slug> is your article's identifier, the same one in its URL. The toll derives
it from the path of the incoming read. The base URL is whatever you give us as
your credits endpoint; the /credits/<slug> shape is fixed.
The route should be public and cacheable. The toll may call it often; a few
minutes of caching (ISR, a CDN, Cache-Control) is plenty. It must not require
auth from the toll beyond an optional shared token if you choose to set one.
The two responses
Tolled: return the credits
For an article you want to charge agents for, return 200 with this JSON:
{
"slug": "the-river-and-the-name",
"title": "The River and the Name",
"contributors": [
{ "authorId": "ravi", "wallet": "0x6b1f…a90c", "weight": 2 },
{ "authorId": "guest", "wallet": "0x9d22…41fe", "weight": 1 }
]
}| Field | Meaning |
|---|---|
slug |
Echo of the slug requested. |
title |
Human title, used in receipts and the operator view. |
contributors |
Who gets paid. At least one entry. |
contributors[].authorId |
Your stable handle for whoever gets paid: an author, or the site itself. |
contributors[].wallet |
The payee's EVM address (0x + 40 hex). Their share settles here. It has to be able to receive USDC on your site's settlement network; an exchange deposit address issued for a different one loses the funds. Optional, so see "an author you have no address for" below. |
contributors[].weight |
Relative share. Omit for an equal split; 2 and 1 means two-thirds and one-third. |
A contributor can stand for a group instead of a single wallet by carrying
members (a nested list of contributors) instead of wallet. The group's share
re-splits among its members by their weights. Use this only if you actually pay a
collective that divides internally; most articles won't need it.
Pages with no author
Plenty of pages have no byline and never will: docs, a changelog, a pricing page, a company blog that ships under one masthead. Nothing in this contract requires a person. Return a single contributor standing for the site, pointed at the wallet you already settle to:
{
"slug": "pricing",
"title": "Pricing",
"contributors": [{ "authorId": "site", "wallet": "0x6b1f…a90c" }]
}authorId is your handle, not ours. It only has to be stable and unique within
your own site, so site, or your domain, is a fine answer when there is nobody
to name. The toll, the license and the receipt behave identically; the money lands
in one wallet instead of being split across several.
If your whole site works this way, you do not need this endpoint at all. Set one payout wallet on the site and every page under your article prefixes tolls to it, with no per-article response to serve. Use this endpoint when you want per-article control over who is paid: free pages, real author splits, a different wallet per piece.
It does not set prices, and it cannot: the response carries no amount. What a page costs is decided by your site's base toll and its per-path price rules, both on the site's What tolls tab. See getting tolled.
Free: return 404
HTTP/1.1 404 Not Found404 is not an error. It is the signal that this article is free. The toll takes a 404 to mean "don't gate this one" and lets every reader through, agent or not. Return 404 for:
- anything you want open to agents,
- drafts and unpublished pieces,
- articles whose author has no wallet (there's nothing to pay into),
- anything behind your own membership or paywall. Don't toll what you already gate another way, or readers pay twice.
Decide this server-side from your own data. The rule of thumb: return credits only when the article is published, public, and has at least one author with a wallet. Otherwise, 404.
Rules that keep it honest
-
Wallets are real addresses. Each
walletmust be a valid EVM address. A malformed address is rejected, not guessed at, and so is the burn address (0x0000…0000), where a transfer succeeds and destroys the money. -
Checksums are worth using. A mixed-case address carries an EIP-55 checksum, so a transposed character in it can be caught. An all-lowercase address carries none, and nothing at any layer can tell it from a typo.
npx naulon checkreports which of yours are in that state. -
Weights are relative, money is exact. The toll splits the fee by weight in whole micro-USDC. You never send amounts or prices through this API, only who and where. Pricing is configured separately.
-
No secrets in the response. This endpoint returns attribution, nothing else. It must never expose settlement secrets, API tokens, or anything an author wouldn't want public. Read it from a privileged path on your side and serve only the safe fields.
-
An author you have no address for: name them, omit the wallet. A contributor may carry an
authorIdand nowalletat all. That says "this person contributed, I don't know where to pay them", and it is strictly better than leaving them out, because we can fill it from an address that author registered with us themselves. That is the whole point of inviting an author: they set their own payout address and every article on your site that names them by thatauthorIdroutes to it, without you ever handling it.The registration is per site, keyed on the
authorIdyou use here, so an author who writes for two sites accepts an invitation on each, and may point each one at a different address. Ids do not have to agree across sites, and a plugin that generates them from local user accounts will not make them agree; nothing depends on it.If nothing can fill it, their weight is absorbed by the contributors that do have addresses, exactly as it always was, and if that empties the list, the article reads free. It never silently pays someone else. Your site's own wallet can catch those shares instead, if you turn that on under Sites → Manage → Unclaimed author shares; it is off by default.
Dropping the contributor entirely still works and still means "free if nobody else is payable". It just throws away the credit and the chance to route it.
Checking it
Two requests tell you it's wired correctly:
# A gated article → 200 with contributors
curl https://your-site.example/api/credits/the-river-and-the-name
# A free or unknown article → 404
curl -i https://your-site.example/api/credits/something-freeIf the first returns the shape above and the second returns a bare 404, the toll has everything it needs. From here, see settlement.