Base di cunniscenza Sviluppatori
Saldu, ricariche è chjavi
Verificà ciò chì avete, ricaricà via Lightning, ricaricà automaticamente da u vostru propiu wallet, vede u coste di ogni dumanda, è gestisce e chjavi da u codice.
Questa pagina hè tradutta in macchina per comodità. L'uriginale in inglese hè a versione chì si applica.
Verificà u saldu
I vostri dui saldi, è quanta parte di u capu di sta chìave hè utilizata.
GET https://nymbot.ai/api/v1/credits/balance — hà bisognu di una chìave API. POST funziona ancu, per i clienti chì u sperpulanu.
balance sì sò i dui saldi insiemu in dollari à u prissu attuale di u Bitcoin, per
strumenti chì aspettanu un numeru solu (null se u prezzu ùn pò micca esse lettu). U restu hè in
crediti è in sats, chì hè cumu i saldi sò effettivamente cunservati. key descrive a chìave
chì hà dumandatu. Una chìave chì hà reachu u so capu pò ancu verificà u saldu.
Risposta
{
"balance": 49.18,
"balance_sats": 42037,
"standard": { "credits": 120.4, "sats": 1204 },
"pro": { "credits": 408.33, "sats": 40833 },
"key": {
"id": "4f0c9a1be27d3856",
"name": "laptop scripts",
"limit_sats": 20000,
"period_used_sats": 3412,
"total_used_sats": 18230,
"reset_period": "monthly",
"reset_at": "2026-10-01T00:00:00Z"
}
}
| Statutu | Quandu |
|---|---|
401 | A chìave hè mancante, sconosciuta, revucata o scaduta. |
cURL
curl https://nymbot.ai/api/v1/credits/balance \
-H "Authorization: Bearer $NYMBOT_API_KEY"
Python
import os
import requests
res = requests.get(
"https://nymbot.ai/api/v1/credits/balance",
headers={"Authorization": "Bearer " + os.environ["NYMBOT_API_KEY"]},
)
balance = res.json()
print(balance["standard"]["sats"], balance["pro"]["sats"])
JavaScript
const res = await fetch("https://nymbot.ai/api/v1/credits/balance", {
headers: { "Authorization": "Bearer " + process.env.NYMBOT_API_KEY },
});
const balance = await res.json();
console.log(balance.standard.sats, balance.pro.sats);
Modi di pagamentu
Cumu pudete ricaricà, è i limiti. Lightning hè l'unicu metodu.
GET https://nymbot.ai/api/v1/topup/payment-methods — micca di chìave necessariu.
Un ricaricu hè da 10 à 1.000.000 di sats; una ricarica Pro deve comprà almeno un creditu Pro, dunque cumencia
à 100 sats. I limiti in dollari seguendu u prezzu di u Bitcoin. bulk_bonus lista u creditu extra n'i ricariche più grandi, accussì cum'è in l'app: 10%, 15% o 20% di più n'e ricariche standard da
500, 1.000 o 5.000 sats, è n'e ricariche Pro da 5.000, 10.000 o 50.000 sats.
Risposta
{
"supported_methods": [
{
"method": "btc-lightning",
"display_name": "Bitcoin Lightning",
"supported_currencies": ["SATS", "USD", "BTC"],
"limits": {
"SATS": { "min": 10, "max": 1000000 },
"USD": { "min": 0.02, "max": 1170 },
"BTC": { "min": 1e-7, "max": 0.01 }
},
"tiers": ["standard", "pro"],
"default_tier": "pro",
"tier_min_sats": { "standard": 10, "pro": 100 },
"sats_per_credit": { "standard": 10, "pro": 100 },
"bulk_bonus": [
{ "bonus": 0.1, "standard_sats": 500, "pro_sats": 5000 },
{ "bonus": 0.15, "standard_sats": 1000, "pro_sats": 10000 },
{ "bonus": 0.2, "standard_sats": 5000, "pro_sats": 50000 }
]
}
]
}
cURL
curl https://nymbot.ai/api/v1/topup/payment-methods
Python
import requests
methods = requests.get("https://nymbot.ai/api/v1/topup/payment-methods").json()
print(methods["supported_methods"][0]["limits"])
JavaScript
const methods = await (await fetch("https://nymbot.ai/api/v1/topup/payment-methods")).json();
console.log(methods.supported_methods[0].limits);
Ricarica via Lightning
Fà una fattura Lightning chì aghjunghje u creditu à u nym à cui appartene a chìave. Pagheteghjà da qualsiasi wallet Lightning, dopu verificà it per avè u creditu aghjuntu.
POST https://nymbot.ai/api/v1/topup/create/btc-lightning — hà bisognu di una chìave API.
| Campu | Tipu | Necessariu | Descrizzione |
|---|---|---|---|
amount | numeru | Iè | Quanto, in currencyUn numeru interu per i sats. |
currency | string | No | SATS (u per default), USD o BTC. I dollari sò cunvertiti à u prezzu attuale di u Bitcoin. |
tier | string | No | pro (u per dëfaut) o u standard: quale bilanciu riceve u creditu. |
Un credit standard hè di 10 sats è un credit Pro hè di 100 sats, più qualsìevu bonus per l'accumuļu;
credits dice ciò chì sta à aghjunghje stu fattura.
Risposta
{
"invoice_id": "b7d41e0c95a2f38e6c1d0e9a4b7f2c61d3e8a05f9b2c4d7e1a6f3b8c0d5e2a9f4",
"payment_request": "lnbc100u1p5...",
"amount_sats": 10000,
"credits": 115,
"tier": "pro",
"expires_at": "2026-09-30T09:27:00Z",
"status": "pending"
}
| Statutu | Quandu |
|---|---|
400 | Un altru metodu in u percorsu (unsupported_method), una moneta sconosciuta (unsupported_currency) o un nivellu, una quantità mancante, o una quantità sottu u minimu (amount_too_small), sopra 1.000.000 di sats (amount_too_large) o rifiutatu da u portafoglio Lightning (amount_out_of_range). |
429 | Più di 60 fatture per questu nime, o 120 da stu indirizzu, in un'ora (rate_limit_exceeded, cù Retry-After). |
502 | Nessuna fattura ùn pò esse fatta avèntu (invoice_unavailable, cù Retry-After). |
cURL
curl https://nymbot.ai/api/v1/topup/create/btc-lightning \
-H "Authorization: Bearer $NYMBOT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"amount": 10000, "currency": "SATS", "tier": "pro"}'
Python
import os
import requests
res = requests.post(
"https://nymbot.ai/api/v1/topup/create/btc-lightning",
headers={"Authorization": "Bearer " + os.environ["NYMBOT_API_KEY"]},
json={"amount": 10000, "currency": "SATS", "tier": "pro"},
)
invoice = res.json()
print(invoice["payment_request"])
JavaScript
const res = await fetch("https://nymbot.ai/api/v1/topup/create/btc-lightning", {
method: "POST",
headers: {
"Authorization": "Bearer " + process.env.NYMBOT_API_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({ amount: 10000, currency: "SATS", tier: "pro" }),
});
const invoice = await res.json();
console.log(invoice.payment_request);
Verificà una ricarica
Chjamu s'a fattura hè stata pagata è, una volta chì u hè, si aghjunghje u creditu. Verificà hè ciò chì u
creditata, dunque dopu à pagà, verificà finchè u statu ùn hè creditedVerificà di novu
dopu hè sicuru: u creditu si deposita una volta sola, quantu una sia u vostru dumanda.
GET https://nymbot.ai/api/v1/topup/status/{invoice_id} — hà bisognu di una chiave da u nùmulu chì hà fattu a fattura.
status è pending (ancora ùn pagatu micca), paid (pagatu, ma micca ancu
accreditatu; ricontrollate), credited (supra u vostru saldu) o u expired (micca pagatu
in tempu). I campi di saldu sò per u livellu chì a fattura rimpiscia.
Risposta
{
"invoice_id": "b7d41e0c95a2f38e6c1d0e9a4b7f2c61d3e8a05f9b2c4d7e1a6f3b8c0d5e2a9f4",
"status": "credited",
"amount_sats": 10000,
"credits": 115,
"tier": "pro",
"expires_at": null,
"balance_credits": 523.33,
"balance_sats": 52333
}
| Statutu | Quandu |
|---|---|
400 | L'ID ùn hè micca l'ID di 64 caratteri di a chjama di creazione. |
404 | Nessuna fattura cù quell'ID per u vostru nym (invoice_not_found). |
cURL
curl https://nymbot.ai/api/v1/topup/status/$INVOICE_ID \
-H "Authorization: Bearer $NYMBOT_API_KEY"
Python
import os, time
import requests
headers = {"Authorization": "Bearer " + os.environ["NYMBOT_API_KEY"]}
url = "https://nymbot.ai/api/v1/topup/status/" + invoice["invoice_id"]
while True:
status = requests.get(url, headers=headers).json()["status"]
if status in ("credited", "expired"):
break
time.sleep(3)
print(status)
JavaScript
const headers = { "Authorization": "Bearer " + process.env.NYMBOT_API_KEY };
const url = "https://nymbot.ai/api/v1/topup/status/" + invoice.invoice_id;
let status;
do {
await new Promise((r) => setTimeout(r, 3000));
status = (await (await fetch(url, { headers })).json()).status;
} while (status !== "credited" && status !== "expired");
console.log(status);
Istoricu di e ricerche
Una riga per ogni dumanda: ch'è stata, quale mudellu, quantu token è ch'è costatu. Nisun prompt o risposta ùn hè conservatu, quindi nisunu hè rimandatu. E righe sò conservate per 90 ghjorni, e più novelle prima. Una chìave chì hà raggiuntu u so capu pò ancu legge a so storia.
GET https://nymbot.ai/api/v1/queries/history — hà bisognu di una chìave API, chì vede i so stessi richesti, o un richestu firmatu da u vostru nùme, chì vede ogni chìave.
| Campu | Tipu | Necessariu | Descrizzione |
|---|---|---|---|
page | numeru interu (ricerca) | No | Per difettu 1, un massimu di 1.000; una pagina più alta hè 400 invalid_value. |
page_count | numeru interu (ricerca) | No | Righe per pagina. Per difettu 20, al più 100. |
start_dateend_date | stringa (ricerca) | No | ISO 8601 date o l'ore. |
model | stringa (ricerca) | No | Solamente stu mudellu. |
type | stringa (ricerca) | No | chat, responses, messages, image, video, speech, transcription o embedding. |
all_keys | bulianu (ricerca) | No | Avec una chiave: true include ogni chìave di u medemu nime. Default false. |
key_id | stringa (ricerca) | No | Cum una dumanda firmatu, o cù all_keys=true: sulamente questa chiave. |
Risposta
{
"data": [
{
"id": "q_71c4e9a0",
"timestamp": "2026-09-30T08:12:00Z",
"model": "anthropic/claude-sonnet-5",
"type": "chat",
"input_tokens": 1240,
"output_tokens": 380,
"cached_tokens": 0,
"cost_sats": 16.2,
"cost_usd": 0.01895,
"balance": "pro",
"key_id": "4f0c9a1be27d3856",
"web_search": false,
"status": "ok"
}
],
"pagination": { "page": 1, "page_count": 20, "total": 311, "total_pages": 16 }
}
cURL
curl "https://nymbot.ai/api/v1/queries/history?page_count=50&type=chat" \
-H "Authorization: Bearer $NYMBOT_API_KEY"
Python
import os
import requests
res = requests.get(
"https://nymbot.ai/api/v1/queries/history",
headers={"Authorization": "Bearer " + os.environ["NYMBOT_API_KEY"]},
params={"page_count": 50, "type": "chat"},
)
for row in res.json()["data"]:
print(row["timestamp"], row["model"], row["cost_sats"])
JavaScript
const res = await fetch("https://nymbot.ai/api/v1/queries/history?page_count=50&type=chat", {
headers: { "Authorization": "Bearer " + process.env.NYMBOT_API_KEY },
});
for (const row of (await res.json()).data) console.log(row.timestamp, row.model, row.cost_sats);
Firmà e dumande di contu
A creazione, a modificazione è a revoca di e chjavi, u riassuntu di u contu è i ricaricamenti automatici ùn pighenu micca una chjave API. Ellu pighena una firma di u vostru nym, dunque una chjave fuggiuta pò spende finu à u so capu ma ùn pò micca creà un'altri chjavi nè aumentà u so propiu capu.
L'applica fa questu per voi: tuttu in u so posto API u fogliu usa questi endpoint. Avete solu bisognu di questa sezione per gestisce i chìavi da u vostru codici.
A firma hè un eventi Nostr di tipu 27235 (NIP-98), mandatu in base64-encoded in u
Authorization testata cù a parolla Nostr davanti:
L'eventu
{
"kind": 27235,
"created_at": 1790726400,
"tags": [
["u", "https://nymbot.ai/api/v1/keys"],
["method", "POST"],
["nonce", "9c4e21f07a3b...16 random bytes in hex"],
["payload", "3f1a0d7c8e2b...sha256 of the exact request body in hex"]
],
"content": "",
"pubkey": "your public key in hex",
"id": "...",
"sig": "..."
}
uhè l'URL chjenu di a dumanda, string di ricerca inclusu, esattamente cum'è mandatu.methodhè u metodu HTTP.payloadhè u SHA-256 di u corpu di a richiesta bruta, in hex. Hè ضرورà nantu àPOSTèPATCH, è u corpu chì mandate deve esse byte per byte quellu chì avete hashatu.created_atdeve esse indistinu à 60 sicondi di l'orologiu di u servitore.- Ogni evenement hè travagliatu una volta, a
GETinclu, dunque un intestazione catturata ùn pò micca esse ripurtata. Firmate una nova per ogni dumanda. Aghjettate unnoncetag cù un valore casale perchì dui richieste firmate in u stessu segundu sò dumandate diverse. - U corpu di una dumanda firmatu pò esse al più 64 KB, è un corpu hà bisognu di
Content-Type: application/json.
Un eventi mancante torna 401 missing_nostr_auth; unu chì hè malformatu, malfirmatu, troppu vecchiu, o per una URL, un metodu o un corpu differente torna
invalid_nostr_auth, cù a ragione in u messaghju; unu riutilizatu ritorna
nostr_auth_replayed. Una chìave API mandata à sti endpoint hè rifiutata. A firma hè verificata prima chì u corpu sia lettu, è ogni indirizzu pò fallisce essa 30 volte par minutu (un indirizzu IPv6 conta cum'è u so tuttu /64); dopu chè u hè 429 cu u
Retry-After.
I navigaturi ponu chjamà sti endpoint solu da i siti propi di Nymbot (https://nymbot.ai,
https://nymchat.app è i so subdomini). Una pagina nantu à quìsi altru siti ùn riceve nisun
header CORS, dunque ùn pò micca legge ciò chì elli ritornanu. I script è e app native, chì ùn mandanu micca
Origin, ùn sò micca influenzati.
A firma hà bisognu di a chìave segreta di u vostru nyme (u nsec), chì cuntrolla
tuttu : a vostra identità, a vostra storia è u vostre saldu. Metteghitelo solu in un script nantu à una
macchina chì vi fidate, leggetelu da l'ambiente invece di scriveghialu in u file, è
preferite l'app quandu pudete.
Quessii aiuti u mbuidu u capu. L'esempi seguenti in sta pagina u utilizanu. Iddunu a chìave segreta in es in u NOSTR_SECRET_HEX; quellu di cURL usa u
nacu strumentu di linea di cumandu, chì piglia una chìave nsec o hex, è sha256sum (su macOS, shasum -a 256).
cURL
nostr_auth() {
method="$1"; url="$2"; body="$3"
nonce=$(openssl rand -hex 16)
if [ -n "$body" ]; then
hash=$(printf '%s' "$body" | sha256sum | cut -d' ' -f1)
event=$(nak event --sec "$NOSTR_SECRET_HEX" -k 27235 -t "u=$url" -t "method=$method" -t "nonce=$nonce" -t "payload=$hash")
else
event=$(nak event --sec "$NOSTR_SECRET_HEX" -k 27235 -t "u=$url" -t "method=$method" -t "nonce=$nonce")
fi
printf 'Nostr %s' "$(printf '%s' "$event" | base64 | tr -d '\n')"
}
Python
# pip install coincurve requests
import base64, hashlib, json, os, time
from coincurve import PrivateKey, PublicKeyXOnly
SECRET = bytes.fromhex(os.environ["NOSTR_SECRET_HEX"])
def nostr_auth(method, url, body=b""):
pubkey = PublicKeyXOnly.from_secret(SECRET).format().hex()
tags = [["u", url], ["method", method], ["nonce", os.urandom(16).hex()]]
if body:
tags.append(["payload", hashlib.sha256(body).hexdigest()])
created_at = int(time.time())
serialized = json.dumps([0, pubkey, created_at, 27235, tags, ""], separators=(",", ":"), ensure_ascii=False)
event_id = hashlib.sha256(serialized.encode()).digest()
event = {
"id": event_id.hex(),
"pubkey": pubkey,
"created_at": created_at,
"kind": 27235,
"tags": tags,
"content": "",
"sig": PrivateKey(SECRET).sign_schnorr(event_id).hex(),
}
return "Nostr " + base64.b64encode(json.dumps(event).encode()).decode()
JavaScript
// npm install nostr-tools
import { createHash, randomBytes } from "node:crypto";
import { finalizeEvent } from "nostr-tools/pure";
const secret = Buffer.from(process.env.NOSTR_SECRET_HEX, "hex");
export function nostrAuth(method, url, body = "") {
const tags = [["u", url], ["method", method], ["nonce", randomBytes(16).toString("hex")]];
if (body) tags.push(["payload", createHash("sha256").update(body).digest("hex")]);
const event = finalizeEvent(
{ kind: 27235, created_at: Math.floor(Date.now() / 1000), tags, content: "" },
secret,
);
return "Nostr " + Buffer.from(JSON.stringify(event)).toString("base64");
}
U resumè di u compte
Cosa mostra u fogliu API di l'app in cima: a vostra chìàve pubblica, i dui saldi, quantu chìàvi sò attive (micca revocate o scadute), è u ricarica automatica impostazioni, o
null quandu u servitore ùn offre micca i fan.
GET https://nymbot.ai/api/v1/account — ha bisognu di un richestu firmatu.
Risposta
{
"data": {
"pubkey": "3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d",
"balances": {
"standard": { "credits": 120.4, "sats": 1204 },
"pro": { "credits": 408.33, "sats": 40833 }
},
"keys_active": 3,
"nwc_auto_topup": {
"connected": true, "threshold_sats": 5000, "topup_sats": 20000, "tier": "pro",
"last_topup_at": null, "last_topup_sats": null, "last_error": null
}
}
}
cURL
URL=https://nymbot.ai/api/v1/account
curl "$URL" -H "Authorization: $(nostr_auth GET "$URL")"
Python
import requests
url = "https://nymbot.ai/api/v1/account"
print(requests.get(url, headers={"Authorization": nostr_auth("GET", url)}).json())
JavaScript
const url = "https://nymbot.ai/api/v1/account";
const res = await fetch(url, { headers: { "Authorization": nostrAuth("GET", url) } });
console.log(await res.json());
Gestione di e chjavi
I punti di termina darettu à a lista di e chì chì di l'app. Tutti quanti hanno bisognu di un riconoscimentu richestuOgni chì hè rimandatu in sta forma, cù i tempi in ISO 8601 è i montanti in sats:
Oggettu chjave
{
"id": "4f0c9a1be27d3856",
"name": "laptop scripts",
"hint": "sk-nymbot-Qm7x…c2Lw",
"limit_sats": 20000,
"reset_period": "monthly",
"reset_at": "2026-10-01T00:00:00Z",
"expire_at": null,
"period_used_sats": 3412,
"total_used_sats": 18230,
"created_at": "2026-08-14T09:21:07Z",
"updated_at": "2026-09-02T17:40:55Z",
"last_used_at": "2026-09-30T08:12:00Z",
"revoked_at": null
}
hint hè sufficienti per ricunoscì una chìave ma micca per usà ela. A chìave stissa hè rimessa
una sola volta, quandu hè fatta.
Elencà e chiavi
GET https://nymbot.ai/api/v1/keys — firmatu.
| Campu | Tipu | Necessariu | Descrizzione |
|---|---|---|---|
include_revoked | bulianu (ricerca) | No | Include e chjavi revocate. Difalsu false. |
Risposta
{ "data": [ { "id": "4f0c9a1be27d3856", "name": "laptop scripts", "hint": "sk-nymbot-Qm7x…c2Lw", "...": "..." } ] }
cURL
URL=https://nymbot.ai/api/v1/keys
curl "$URL" -H "Authorization: $(nostr_auth GET "$URL")"
Python
import requests
url = "https://nymbot.ai/api/v1/keys"
for key in requests.get(url, headers={"Authorization": nostr_auth("GET", url)}).json()["data"]:
print(key["id"], key["name"], key["period_used_sats"], key["limit_sats"])
JavaScript
const url = "https://nymbot.ai/api/v1/keys";
const { data } = await (await fetch(url, { headers: { "Authorization": nostrAuth("GET", url) } })).json();
for (const key of data) console.log(key.id, key.name, key.period_used_sats, key.limit_sats);
Fà una chjave
POST https://nymbot.ai/api/v1/keys — firmatu. Ritorne 201.
| Campu | Tipu | Necessariu | Descrizzione |
|---|---|---|---|
name | string | Iè | Da 1 à 40 caratteri, diffirente da e vostre altre tasti attivi (ignorendu a maiuscola). |
limit_sats | integru | No | U capu di a spesa in sats, almenu 1. Lasciatè u fora per micca capu. |
reset_period | string | No | daily, weekly o monthly. Bisogni limit_satsLacciatelu fora per un capu chì ùn si resetta mai. |
expire_at | string o integer | No | Quandu a chìave smette di funziona: un tempu ISO 8601, o millisecondi da u 1970. |
Risposta (201)
{
"data": {
"id": "4f0c9a1be27d3856",
"name": "laptop scripts",
"hint": "sk-nymbot-Qm7x…c2Lw",
"limit_sats": 20000,
"reset_period": "monthly",
"key": "sk-nymbot-Qm7x...c2Lw",
"...": "the rest of the key object"
}
}
| Statutu | Quandu |
|---|---|
400 | Un nome mancante o troppo longu; un nome digià utilizatu (duplicate_name); un capu chì ùn hè micca un numeru interu di 至 least 1; una periodu di reset senza un capu; una scadenza in u passatu; un campu ùn cunsciutu (unknown_parameter); o 25 chjavi attive hè già (too_many_keys). |
429 | Più di 60 chjavi fatte da stu nime, o 120 da stu indirizzu, in un'ura (rate_limit_exceeded, cù Retry-After). |
cURL
URL=https://nymbot.ai/api/v1/keys
BODY='{"name":"laptop scripts","limit_sats":20000,"reset_period":"monthly"}'
curl "$URL" \
-H "Authorization: $(nostr_auth POST "$URL" "$BODY")" \
-H "Content-Type: application/json" \
-d "$BODY"
Python
import json
import requests
url = "https://nymbot.ai/api/v1/keys"
body = json.dumps({"name": "laptop scripts", "limit_sats": 20000, "reset_period": "monthly"}).encode()
res = requests.post(
url,
data=body,
headers={"Authorization": nostr_auth("POST", url, body), "Content-Type": "application/json"},
)
print(res.json()["data"]["key"])
JavaScript
const url = "https://nymbot.ai/api/v1/keys";
const body = JSON.stringify({ name: "laptop scripts", limit_sats: 20000, reset_period: "monthly" });
const res = await fetch(url, {
method: "POST",
headers: { "Authorization": nostrAuth("POST", url, body), "Content-Type": "application/json" },
body,
});
console.log((await res.json()).data.key);
Leggenda una chiave
GET https://nymbot.ai/api/v1/keys/{id} — firmatu.
Ritorne {"data": {…}} cu l'oggettu chìa, o 404
key_not_found se nisuna chìave vostra hà quellu ID.
cURL
URL=https://nymbot.ai/api/v1/keys/4f0c9a1be27d3856
curl "$URL" -H "Authorization: $(nostr_auth GET "$URL")"
Python
import requests
url = "https://nymbot.ai/api/v1/keys/4f0c9a1be27d3856"
print(requests.get(url, headers={"Authorization": nostr_auth("GET", url)}).json()["data"])
JavaScript
const url = "https://nymbot.ai/api/v1/keys/4f0c9a1be27d3856";
console.log((await (await fetch(url, { headers: { "Authorization": nostrAuth("GET", url) } })).json()).data);
Chjangeà una chiave
PATCH https://nymbot.ai/api/v1/keys/{id} — firmatu.
Mandate tutt'ellu chì name, limit_sats, reset_period è
expire_at, cù e stesse regole comu quandu si fà una chìave. null scurza un
campu: senza limite, senza reset, senza scadenza. Cambià u periodu di reset cumencia un novu periodu da zero. Una
chia revuocata ùn pò micca esse cambiata (400 key_revoked). Ritorna
{"data": {…}} cum u oggettu di chiave aghjornatu.
cURL
URL=https://nymbot.ai/api/v1/keys/4f0c9a1be27d3856
BODY='{"limit_sats":50000,"expire_at":null}'
curl -X PATCH "$URL" \
-H "Authorization: $(nostr_auth PATCH "$URL" "$BODY")" \
-H "Content-Type: application/json" \
-d "$BODY"
Python
import json
import requests
url = "https://nymbot.ai/api/v1/keys/4f0c9a1be27d3856"
body = json.dumps({"limit_sats": 50000, "expire_at": None}).encode()
res = requests.patch(
url,
data=body,
headers={"Authorization": nostr_auth("PATCH", url, body), "Content-Type": "application/json"},
)
print(res.json()["data"])
JavaScript
const url = "https://nymbot.ai/api/v1/keys/4f0c9a1be27d3856";
const body = JSON.stringify({ limit_sats: 50000, expire_at: null });
const res = await fetch(url, {
method: "PATCH",
headers: { "Authorization": nostrAuth("PATCH", url, body), "Content-Type": "application/json" },
body,
});
console.log((await res.json()).data);
Rivocà una chìave
DELETE https://nymbot.ai/api/v1/keys/{id} — firmatu.
Ferma a chìave subitu, per sempre. Resta in a lista cù revoked_at statu, è pò
esse vistu cù include_revoked=trueRevoca una chiave chì hè già revucata risponde
in u mediu modu. Sadece e più novelli 50 chiavi revucate sò mantenute; e più vecchie sò eliminate quandu una altra chiave
hè revucata.
Risposta
{ "data": { "id": "4f0c9a1be27d3856", "revoked": true } }
cURL
URL=https://nymbot.ai/api/v1/keys/4f0c9a1be27d3856
curl -X DELETE "$URL" -H "Authorization: $(nostr_auth DELETE "$URL")"
Python
import requests
url = "https://nymbot.ai/api/v1/keys/4f0c9a1be27d3856"
print(requests.delete(url, headers={"Authorization": nostr_auth("DELETE", url)}).json())
JavaScript
const url = "https://nymbot.ai/api/v1/keys/4f0c9a1be27d3856";
const res = await fetch(url, { method: "DELETE", headers: { "Authorization": nostrAuth("DELETE", url) } });
console.log(await res.json());
Ricarica automatica NWC
Connettate un wallet Lightning cù Nostr Wallet Connect è Nymbot ricarica u saldu da sola quandu a spesa di l'API u rende bassu. U fogliu API di l'app hà e stessesi impostazioni; questi sò i punti d'accesso dietro di essa. Tutti quanti anu bisognu di una richestu firmatu.
Cumu funziona: dop chì una richiesta API sia decurtata da u saldu chì avete sceltu per monitorà, s'ellu questu saldu hè calatu sottu à a vostra soglia, Nymbot crea una fattura per a vostra quantità di ricarica, si dumanda à u vostru wallet di pagh'ellu, è aghjunghje u creditu. Ellu ricarica al più una volta ogni 5 minuti per ogni nym è saldu, cusì una serie di richieste ùn pò micca svuotà u wallet. A spesa in e app ùn attivà micca stu prucessu. U tempu è a dimensione di l'ultima ricarica, è l'ultimu errore, sò in i settings; s'ellu un pagamentu hè passatu dopu un errore, verificà a so fattura cù lu statu di a ricarica creditu it.
Una stringa di cunnessione permette à quellischi chì a tengenu di dumandà à u vostru wallet di pagà. Nymbot
stocca essa criptata è a usa solu per pagà e i so scontrini di ricarica, ma fate
una cunnessione solu per questu, cù un budget di spesa in u vostru wallet, in modu chì u massimu chì puderia pagà sia un numeru chì avete chjostu. U wallet deve esse supportà pay_invoice.
Connessione di un portafoglio
POST https://nymbot.ai/api/v1/nwc-auto-topup/connect — firmatu.
| Campu | Tipu | Necessariu | Descrizzione |
|---|---|---|---|
nwc_url | string | Iè | A stringa di cunnessione, cuminciendu nostr+walletconnect://. Nymbot chiede à u wallet get_info prima di salvà ellu, è u stocca criptatu. |
threshold_sats | integru | Iè | Ricaricà quandu u saldu scende sottu à stu numeru di sats. Almenu 1,000. |
topup_sats | integru | Iè | Quantu à aghjunghje ogni volta. 1,000 à 1,000,000 sats. |
tier | string | No | pro (u per dëfaut) o u standardu saldu da vede è da ricaricà. |
Risposta
{
"data": {
"connected": true,
"threshold_sats": 5000,
"topup_sats": 20000,
"tier": "pro",
"last_topup_at": null,
"last_topup_sats": null,
"last_error": null
}
}
| Statutu | Quandu |
|---|---|
400 | Ùn hè micca una stringa di cunnessione (invalid_nwc_url); u portafoglio ùn risponde ùn u so relay (nwc_unreachable) o si rifiuta u check (nwc_rejected); a cunnessione ùn pò micca pagà e fatture (nwc_missing_permission); o un montante fora di i limiti. |
501 | I ricaricamenti automatici ùn sò micca attivati per stu serviziu (nwc_unavailable). A a stessa cosa si applica hè à l'altri dui punti di terminazione. |
cURL
URL=https://nymbot.ai/api/v1/nwc-auto-topup/connect
BODY='{"nwc_url":"nostr+walletconnect://...","threshold_sats":5000,"topup_sats":20000,"tier":"pro"}'
curl "$URL" \
-H "Authorization: $(nostr_auth POST "$URL" "$BODY")" \
-H "Content-Type: application/json" \
-d "$BODY"
Python
import json, os
import requests
url = "https://nymbot.ai/api/v1/nwc-auto-topup/connect"
body = json.dumps({
"nwc_url": os.environ["NWC_URL"],
"threshold_sats": 5000,
"topup_sats": 20000,
"tier": "pro",
}).encode()
res = requests.post(
url,
data=body,
headers={"Authorization": nostr_auth("POST", url, body), "Content-Type": "application/json"},
)
print(res.json())
JavaScript
const url = "https://nymbot.ai/api/v1/nwc-auto-topup/connect";
const body = JSON.stringify({
nwc_url: process.env.NWC_URL,
threshold_sats: 5000,
topup_sats: 20000,
tier: "pro",
});
const res = await fetch(url, {
method: "POST",
headers: { "Authorization": nostrAuth("POST", url, body), "Content-Type": "application/json" },
body,
});
console.log(await res.json());
Leghenu i paramentri
GET https://nymbot.ai/api/v1/nwc-auto-topup — firmatu.
Riduca u stessu oggettu chì u cunnessione, cù connected: false è i resti di i campi
null quandu ùn ci hè nente wallet cunnessu. A stringa di cunnessione in itself ùn hè mai
rimessa.
cURL
URL=https://nymbot.ai/api/v1/nwc-auto-topup
curl "$URL" -H "Authorization: $(nostr_auth GET "$URL")"
Python
import requests
url = "https://nymbot.ai/api/v1/nwc-auto-topup"
print(requests.get(url, headers={"Authorization": nostr_auth("GET", url)}).json())
JavaScript
const url = "https://nymbot.ai/api/v1/nwc-auto-topup";
console.log(await (await fetch(url, { headers: { "Authorization": nostrAuth("GET", url) } })).json());
Diconnessione
DELETE https://nymbot.ai/api/v1/nwc-auto-topup/connection — firmatu.
Elimina a stringa di cunnessione sturata. Micca più ricariche sò fatte. Per esse sicuru, pudete ancu revoca a cunnessione in u vostru wallet.
Risposta
{ "data": { "connected": false, "threshold_sats": null, "topup_sats": null, "tier": null, "last_topup_at": null, "last_topup_sats": null, "last_error": null } }
cURL
URL=https://nymbot.ai/api/v1/nwc-auto-topup/connection
curl -X DELETE "$URL" -H "Authorization: $(nostr_auth DELETE "$URL")"
Python
import requests
url = "https://nymbot.ai/api/v1/nwc-auto-topup/connection"
print(requests.delete(url, headers={"Authorization": nostr_auth("DELETE", url)}).json())
JavaScript
const url = "https://nymbot.ai/api/v1/nwc-auto-topup/connection";
const res = await fetch(url, { method: "DELETE", headers: { "Authorization": nostrAuth("DELETE", url) } });
console.log(await res.json());
Pagamentu per dumanda senza una chiave
I punti terminali à u prezzu fissu pon trà paghà per una dumanda à la volta via Lightning, senza chì chìave,
senza contu è senza saldu: POST /images/generations, POST /images/edits,
POST /videos, POST /audio/speech, POST /audio/transcriptions,
POST /audio/translations è POST /embeddings. Chat, Risposte è
Messaggi anu sempre bisognu di una chìave. Una richiesta chì porta una chìave hè fatturata à u saldu cum'è u normale; u
fluxu di pagamentu cumincia solu quandu ùn hè mandata nisuna chìave.
Nymbot parla dui versione di a stissa idea, da un solu backend: Lightning Labs'
L402 (accettatu pure sottu u so vechi nomu, LSAT) è u draft di l'IETF
Pagamentu Schema d'autenticazione HTTP cù u lightning metudu è
charge intenzione. Usa quella chì u vostru cliente capisce.
Pagamentu senza una chìave hè attivatu solu quandu API_L402_SECRET tene almenu 32 byte randum, cum u hex (64 caratteri) o base64 (44). Fàne unu cù openssl rand -hex 32Un valore più curtu o di guessable spegne a funzione è registra u motivu. Per rotà ite, move u vechju
valore à API_L402_SECRET_PREVIOUS per un ghjornu: e credenziali, i URL di statu è
e sfide fatte sottu à ellu continuanu à funziona finchè ùn espiranu micca.
A sfida
Mandate a dumanda senza nente Authorization intestazione. S'ellu hè validu, niente ùn corre, è tù ricevi 402 Payment Required cu una fattura per esattamente ciò chì quella dumanda costa:
u stessu prezzu chì una chìe pagherebbe, cunvertitu à 10 sats per un creditu standard o 100 sats per un creditu Pro
è arrotondatu à un sat interu (almenu 1 sat, è almenu u minimu di 0.05 creditu). A
risposta porta trè WWW-Authenticate sfide per a stissa fattura:
HTTP/1.1 402 Payment Required
Content-Type: application/problem+json; charset=utf-8
Cache-Control: no-store
WWW-Authenticate: L402 macaroon="AgJC...", invoice="lnbc2370n1..."
WWW-Authenticate: LSAT macaroon="AgJC...", invoice="lnbc2370n1..."
WWW-Authenticate: Payment id="kM9x...", realm="nymbot", method="lightning", intent="charge",
request="eyJhbW91bnQiOiIyMzciLC...", description="Nymbot API POST /images/generations (237 sats)",
digest="sha-256=:X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE=:", expires="2026-09-30T12:15:00.000Z",
opaque="eyJlbmRwb2ludCI6IlBPU1QgL2ltYWdlcy9nZW5lcmF0aW9ucyJ9"
{
"type": "https://paymentauth.org/problems/payment-required",
"title": "Payment Required",
"status": 402,
"detail": "This request costs 237 sats. Pay the Lightning invoice, then send the identical request again ...",
"challengeId": "kM9x...",
"amount_sats": 237,
"invoice": "lnbc2370n1...",
"payment_hash": "9db1370f...",
"expires_at": "2026-09-30T12:15:00.000Z",
"error": { "message": "This request costs 237 sats. ...", "type": "payment_required", "code": "payment_required", "param": null }
}
U pagamentu request u parametru hè un JSON base64url:
{"amount":"237","currency":"sat","methodDetails":{"invoice":"lnbc...","network":"mainnet","paymentHash":"..."}}.
Una sfida hè ligata à u puntu terminale, à u Content-Type (u soitu di u media è, per
multipart, u so limite) è à u SHA-256 di i byte esatti di u corpu chì avete mandatu, è dura 15
minuti. Dopu u pagamentu, mandate u identicu richiamate di novu: u stessu
Content-Type è i stessi byte JSON, o per i termini multipart
(/images/edits, /audio/transcriptions, /audio/translations)
lu stessu corpu multipart cù u stessu limite. A maiur parti di e librerie HTTP pighenu un novu limite ogni
volta chì elli codifichenu una forma, dunque codificatelu una volta è mandate ste byte dui volte.
Ogni indirizzu pò dumandà 30 sfide per minutu (un indirizzu IPv6 conta cum'è u so interu /64).
E richieste u cui l'indirizzu ùn hè micca cunosciu condividenu una norma più stritta di 10 per minutu, è ci hè un capu generale
supra e sfide chì Nymbot emette trà tutti l'indirizzi; una richiesta rifiutata prima chì una sfida sia
fatta (per esempiu cù un corpu chì ùn hè micca un JSON validu) ùn conta micca per ellu. Oltre à isso
a risposta hè 429 cu u Retry-After, mandatu prima chì u corpu sia lettu. I
puntoni chjamati senza una chiave o un credenziali contanu ancu in u limitiu generale di 120
richieste senza autenticazione par minutu per indirizzu. Una credenziale hè verificata prima chì u corpu sia
lettu, è una richiesta chì u Content-Type è micca application/json (o
multipart/form-data per i caricamenti) hè rifiutatu cù 415 è ùn riceve mai
una fattura.
I pizzi di l'embeddings sò basati nantu à una stima di i token in l'input, cù un margine di 1.5×, perchè u conteggiu reale hè saputu solu dopu. Vuje pagate per i token effettivamente utilizati, è a parti di u pagamentu micca utilizata torna versu di voi cum'è un rimbusu di u token.
Mandendu u pagamentu
Paghe a fattura cù quessiunque wallet Lightning. U wallet vi dà a preimage, 64 caratteri esadecimali. Poi mandate a stessa dumanda cù unu di questi:
| Schema | Intitulatu |
|---|---|
| L402 | Authorization: L402 <macaroon>:<preimage> (LSAT funziona ancu ) |
| Pagamentu | Authorization: Payment <base64url JSON>, indu hè u JSON {"challenge": {every parameter of the challenge, as sent}, "payload": {"preimage": "<hex>"}} |
Una dumanda pagata risponde esattamente cum'una fatta cù una chìave, eccettu chì a nymbot
l'oggettu ùn hà micca campu di saldu: {"payment": "l402", "tier": "pro", "paid_sats": 237,
"charged_sats": 237}, è ùn ci hè micca X-Nymbot-Balance-Sats intestazione. Una
richesta pagata cù u schema di Pagamentu riceve ancu un Payment-Receipt intestazione (base64url
JSON cù l'ID di a sfida, u hash di u pagamentu cum'è reference, status è
timestamp). E dumande paghate ùn sò ligati à alcun nym, dunque ùn si vedanu micca in a
storia di a ricerca.
| Statutu | Quandu |
|---|---|
402 payment_already_used | Ogni pagamentu paga per una dumanda. A risposta hè una nova sfida per sta dumanda, dunque un cliente chì mette in cache a so ultima credenziale (cum'à lnget paga simplicemente di novu. |
402 payment_mismatch | L'autenticazione hè stata emessa per un altru endpoint, Content-Type ou corpu, ou paga meno di ciò chì sta dumanda costa avà. Una nova sfida per sta dumanda vene cù essa; se u pagamentu era troppu picculu, ciò chì avete pagatu torna cum'è un rimbusu di u token (refund_token è refund_sats in u corpu). |
402 payment_expired | Più di 15 minuti sò passati da a sfida. Una nova sfida vene cù essa. S'a preimaghjine mostra chì avete pagatu, ciò chì avete pagatu torna cum'è un rimbusu di u token (refund_token è refund_sats in u corpu), una volta; a credenziale hè dopu à esse usata. |
401 invalid_preimage | L'imaghjina precedente ùn hè micca hashata per u hash di pagamentu di a fattura. U pagamentu ùn hè micca usatu. |
401 invalid_payment_credential | A credenziale hè malformata, hè stata cambiata dopu à ciò chì Nymbot l'ha emessa, o nomina un hash di pagamentu per u quale Nymbot ùn hà mai emesso una fattura. Un macaroon cù una caveat chì Nymbot ùn conosce micca, o cù caveats cunflittuali, hè rifiutatu. |
429 rate_limit_exceeded | Più di 30 credenziali o chjavi chì anu fallitu a verificazione sò venuti da stu indirizzu in un minutu, o un token di rimpursu hè statu mandatu più di 60 volte in un minutu. Aspetta per Retry-After. |
cURL
BODY='{"model":"nano-banana","prompt":"a lighthouse at dusk","response_format":"b64_json"}'
URL=https://nymbot.ai/api/v1/images/generations
# 1. Get the challenge
CH=$(curl -s -D - -o /dev/null "$URL" -H "Content-Type: application/json" -d "$BODY" | grep -i '^www-authenticate: L402')
MAC=$(echo "$CH" | sed -E 's/.*macaroon="([^"]+)".*/\1/')
INVOICE=$(echo "$CH" | sed -E 's/.*invoice="([^"]+)".*/\1/')
# 2. Pay $INVOICE with your wallet and copy the preimage
PREIMAGE=...
# 3. Send the identical request with the credential
curl "$URL" -H "Content-Type: application/json" -H "Authorization: L402 $MAC:$PREIMAGE" -d "$BODY"
lnget
# lnget (Lightning Labs) pays L402 challenges from your own lnd node and retries for you
lnget -X POST -H "Content-Type: application/json" \
-d '{"model":"nano-banana","prompt":"a lighthouse at dusk","response_format":"b64_json"}' \
https://nymbot.ai/api/v1/images/generations
Python
import base64, json, re
import requests
url = "https://nymbot.ai/api/v1/images/generations"
body = json.dumps({"model": "nano-banana", "prompt": "a lighthouse at dusk", "response_format": "b64_json"}).encode()
headers = {"Content-Type": "application/json"}
challenge = requests.post(url, data=body, headers=headers)
assert challenge.status_code == 402
info = challenge.json()
print("Pay", info["amount_sats"], "sats:", info["invoice"])
preimage = pay_with_your_wallet(info["invoice"]) # 64 hex characters
# L402
mac = re.search(r'L402 macaroon="([^"]+)"', challenge.headers["WWW-Authenticate"]).group(1)
res = requests.post(url, data=body, headers={**headers, "Authorization": f"L402 {mac}:{preimage}"})
print(res.json()["nymbot"])
JavaScript
const url = "https://nymbot.ai/api/v1/images/generations";
const body = JSON.stringify({ model: "nano-banana", prompt: "a lighthouse at dusk", response_format: "b64_json" });
const headers = { "Content-Type": "application/json" };
const challenge = await fetch(url, { method: "POST", headers, body });
const www = challenge.headers.get("www-authenticate");
// The Payment scheme: echo every challenge parameter back with the preimage
const start = www.indexOf("Payment ");
const params = Object.fromEntries([...www.slice(start + 8).matchAll(/(\w+)="((?:[^"\\]|\\.)*)"/g)].map((m) => [m[1], m[2]]));
const { invoice } = JSON.parse(Buffer.from(params.request, "base64url").toString()).methodDetails;
const preimage = await payWithYourWallet(invoice);
const credential = Buffer.from(JSON.stringify({ challenge: params, payload: { preimage } })).toString("base64url");
const res = await fetch(url, { method: "POST", headers: { ...headers, Authorization: `Payment ${credential}` }, body });
console.log((await res.json()).nymbot, res.headers.get("payment-receipt"));
Clienti bâtiti su mppx cu un handle di metodu Lightning gestisce a sfida di Pagamentu
ellisi stessi; puntateli versu l'endpoint è lasciateli paghjà.
Vidii
Pagatu POST /videos risposte 202 cumtene una chìave, più un
status_url: GET senza alcuna chìave per seguì u travagliu. Hè firmatu
è funziona per 24 ore, tantu chì u travagliu sia mantenutu.
{
"id": "vid_...",
"status": "in_progress",
"status_url": "https://nymbot.ai/api/v1/videos/vid_...?exp=1790000000&sig=...",
"nymbot": {
"payment": "l402", "tier": "pro", "paid_sats": 4800, "charged_sats": 4800,
"refund_token": "REFUND-5E0B..."
}
}
Mantene u refund_token da sta risposta: hè mostratu solu quì. Hè vacante mentra
u video si renderizza (GET /api/v1/l402/refunds risposte "status": "pending");
si u render fallisce senza fatturatu, u pagamentu cade sopra ellu. L'URL di u statu mostra
refund_sats per un travagli rimborsatu ma mai u token, dunque u cumpartuvaghju di l'URL di u statu ùn cumpartaghja micca u rimboru. U verificà u token risolve ancu un travagli fallitu chì nisun ùn hà interrogatu.
Rimborzi
Sì una dumanda pagata fallisce è u furnitore fattura à Nymbot per u tentativu, u pagamentu hè ritentu è
l'erroru u dice, cù charged_sats, esattamente cum'è per una dumanda chjunta. S'ellu fallisce
senza esse fatturatu, l'erroru porta un rimbusu di u token vale quantu avete pagatu:
{
"error": {
"message": "The image generator failed. Nothing was charged. Please try again. The 237 sats you paid are on refund token REFUND-...",
"type": "api_error",
"code": "upstream_error",
"refund_token": "REFUND-0C15DBED145D59131BA70298A413ADEB3D9B9AB082C476EA70A5BD38FCA5DE6D",
"refund_sats": 237,
"refund_expires_at": "2026-10-30T12:00:00.000Z"
}
}
I pezzi ùn utilizati ritornanu in u stessu modu: s'avìate dumandatu dui imaghjini è una hè fallitu senza esse fatturata,
a risposta di successu di nymbot l'oggettu porta un tòccu di rimbuorsu per quellu ca manca;
una trascrizione a chì a so lunghezza ùn pudia micca esse letta prima hè prissata per a lunghezza massima chì u file
puderia avè (mai più di 30 minuti), è a differenza cù a lunghezza reale torna cum'è un
tòccu di rimbuorsu; s'ellu si scopra chì hè più longu di 30 minuti, hè rifiutatu cù 413
è tuttu u pagamentu torna. L'embeddings rimandanu ciò chì a stima avìa ritiratu. Un videu fallimentu rimborsanu u token chì a so sottomissione avìa rimandatu.
Un token di rimborso hè un codice aleatoriu di 256 bit. Nymbot custodisce solu u so hash, è scade dopu 30 ghjorni. Ellu cuntene un saldu di sat, è pudete:
- Paghe cù questu. Mandà
Authorization: Bearer REFUND-…su quessiunque di i termini sopra citati (un SDK di OpenAI u piglia cum'è a so chìave API). U prezzu hè calcolatu in base à u token è ciò chì resta resta nantu à ellu (refund_token_satsin unymbotoggettu). Un tòkemu chì vale meno di e risposte di a dumanda402refund_insufficient; un fallimentu micca fatturato rimette i sats nantu à u stessu token. - Verificà
GET /api/v1/l402/refundscum a stessa intestazione ritorna{"sats": 237, "status": "open", "expires_at": "..."}. - Un token pò esse usatu al più 60 volte par minutu.
- Trasferiscila à un nym. Incollatu in Riscattà un regalu in u
app Nymbot, o chjamate
POST /api/v1/l402/refunds/redeemcu un u richestu firmatu è{"refund_token": "REFUND-...", "balance": "standard"}(o"pro"). I crediti interi andanu à u saldu (10 sats ciasun biri in standard, 100 in Pro); i sats chì ùn fanu micca un creditu interu restanu nantu à u token per e richieste API.
cURL
URL=https://nymbot.ai/api/v1/l402/refunds/redeem
BODY='{"refund_token":"REFUND-...","balance":"standard"}'
curl "$URL" \
-H "Authorization: $(nostr_auth POST "$URL" "$BODY")" \
-H "Content-Type: application/json" \
-d "$BODY"
Risposta
{ "data": { "credited": 23, "tier": "standard", "balance_credits": 123, "remaining_sats": 7 } }