Kennisbank Ontwikkelaars
Balance, top-ups en sleutels
Controleer wat je hebt, top up over Lightning, top up automatisch uit je eigen portemonnee, zie wat elke aanvraag kost en beheer sleutels uit code.
Deze pagina is voor het gemak automatisch vertaald. Het Engelse origineel is de versie die van toepassing is.
Controleer de balans
Beide van uw balans, en hoeveel van deze sleutel de hoes wordt gebruikt.
GET https://nymbot.ai/api/v1/credits/balance Je hebt een API sleutel nodig. POST Het werkt ook voor klanten die het verwachten.
balance is de twee saldi samen in dollars tegen de huidige Bitcoin-prijs, voor tools die een enkel getal verwachten (null De rest is in credits en sats, dat is hoe de saldi daadwerkelijk worden bewaard. key beschrijft de sleutel die is gevraagd.Een sleutel die zijn cap heeft bereikt, kan nog steeds het saldo controleren.
Reactie
{
"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"
}
}
| Status | Wanneer |
|---|---|
401 | De sleutel is ontbreken, onbekend, ingetrokken of verlopen. |
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);
Betalingsmethoden
Hoe je kunt top, en de grenzen. bliksem is de enige methode.
GET https://nymbot.ai/api/v1/topup/payment-methods Geen sleutel nodig.
Een top-up is 10 tot 1.000.000 sats; een Pro-top-up moet ten minste één Pro-credit kopen, dus het begint bij 100 sats. bulk_bonus Hieronder staat het extra krediet op grotere top-ups, hetzelfde als in de app: 10%, 15% of 20% meer op standaard top-ups van 500, 1.000 of 5.000 sats, en op Pro top-ups van 5.000, 10.000 of 50.000 sats.
Reactie
{
"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);
Toppen boven de bliksem
Maak een Lightning-factuur die krediet toevoegt aan de nym waaraan de sleutel behoort. Controleer het Om het krediet toe te voegen.
POST https://nymbot.ai/api/v1/topup/create/btc-lightning Je hebt een API sleutel nodig.
| Het veld | type | vereist | Beschrijving |
|---|---|---|---|
amount | Aantal | Ja | Hoeveel, in currencyEen heel getal voor sats. |
currency | String | niet | SATS (Het is een defect) USD of BTCDollars worden omgezet naar de huidige Bitcoin prijs. |
tier | String | niet | pro (de default) of standard: naar welke balans het krediet gaat. |
Een standaard credit is 10 sats en een Pro credit 100 sats, plus een bulk bonus;
credits zegt wat deze factuur zal toevoegen.
Reactie
{
"invoice_id": "b7d41e0c95a2f38e6c1d0e9a4b7f2c61d3e8a05f9b2c4d7e1a6f3b8c0d5e2a9f4",
"payment_request": "lnbc100u1p5...",
"amount_sats": 10000,
"credits": 115,
"tier": "pro",
"expires_at": "2026-09-30T09:27:00Z",
"status": "pending"
}
| Status | Wanneer |
|---|---|
400 | Een andere methode op de weg (unsupported_method, een onbekende munt (unsupported_currency) of tier, een ontbrekende hoeveelheid, of een bedrag onder het minimum (amount_too_small, meer dan 1 000 000 sats (amount_too_large) of geweigerd door de Lightning wallet (amount_out_of_range). |
429 | Meer dan 60 facturen voor deze nimm, of 120 van dit adres, in een uur (rate_limit_exceeded, met Retry-After). |
502 | Er kan op dit moment geen factuur worden gemaakt (invoice_unavailable, met 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);
Check een top-up
Vraagt of de factuur is betaald en, zodra het heeft, voegt het krediet toe. Checking is wat het krediet, dus na het betalen, controleer tot de status is creditedControleer daarna opnieuw is veilig: de kredietlanding eenmaal, hoe vaak je ook vraagt.
GET https://nymbot.ai/api/v1/topup/status/{invoice_id} Je hebt een sleutel nodig van de nym die de factuur heeft gemaakt.
status is pending (nog niet betaald) paid (betaald, maar nog niet gecrediteerd; controleer opnieuw), credited (op uw balans) of expired (niet tijdig betaald).De saldovelden zijn voor de laag waar de factuur bovenop staat.
Reactie
{
"invoice_id": "b7d41e0c95a2f38e6c1d0e9a4b7f2c61d3e8a05f9b2c4d7e1a6f3b8c0d5e2a9f4",
"status": "credited",
"amount_sats": 10000,
"credits": 115,
"tier": "pro",
"expires_at": null,
"balance_credits": 523.33,
"balance_sats": 52333
}
| Status | Wanneer |
|---|---|
400 | De id is niet de 64-karakter-id van de creatieoproep. |
404 | Geen factuur door die id voor je 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);
Wilt geschiedenis
Een rij per verzoek: wat het was, welk model, hoeveel tokens en wat het kost. Er worden geen aanwijzingen of antwoorden bewaard, dus geen worden geretourneerd. Rijen worden 90 dagen bewaard, nieuwste eerst. Een sleutel die zijn cap heeft bereikt, kan nog steeds zijn geschiedenis lezen.
GET https://nymbot.ai/api/v1/queries/history — heeft een API-sleutel nodig, die zijn eigen verzoeken ziet, of een Ondertekend verzoek van je nim, die elke sleutel ziet.
| Het veld | type | vereist | Beschrijving |
|---|---|---|---|
page | De vraag (query) | niet | Standaard 1, maximaal 1000; een hogere pagina is 400 invalid_value. |
page_count | De vraag (query) | niet | Raden per pagina. Standaard 20, maximaal 100. |
start_dateend_date | De string (query) | niet | ISO 8601 datums of tijden. |
model | De string (query) | niet | Alleen dit model. |
type | De string (query) | niet | chat, responses, messages, image, video, speech, transcription of embedding. |
all_keys | Booleaanse taal (query) | niet | Met een sleutel: true bevat elke sleutel van dezelfde nym. false. |
key_id | De string (query) | niet | Met een ondertekend verzoek, of met all_keys=trueAlleen deze sleutel. |
Reactie
{
"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);
Account aanvragen ondertekenen
Het maken, wijzigen en herroepen van sleutels, het accountoverzicht en automatische top-ups nemen geen API-sleutel. ze nemen een handtekening van uw nym, zodat een gelekte sleutel tot zijn cap kan besteden, maar nooit een andere sleutel kan maken of zijn eigen cap kan verhogen.
De app doet dit voor u: alles in zijn Vuur Je hebt deze sectie alleen nodig om sleutels uit je eigen code te beheren.
De handtekening is een Nostr-gebeurtenis van soort 27235 (NIP-98), verzonden base64-gecodeerd in de
Authorization Header met het woord Nostr Aan de voorzijde:
Het evenement
{
"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": "..."
}
uis de volledige URL van de verzoek, query string inbegrepen, precies zoals verzonden.methodDit is de HTTP methode.payloadis de SHA-256 van het ruwe verzoek lichaam, in hex.POSTEnPATCH, en het lichaam dat u verzendt moet byte voor byte zijn dat u hashte.created_atmoet binnen 60 seconden van de klok van de server zijn.- Elke gebeurtenis werkt één keer, a
GETinclusief, zodat een vastgelegde kop kan niet opnieuw worden afgespeeld. Schrijf een nieuwe voor elke aanvraag. Voeg eennoncetag met een willekeurige waarde, zodat twee verzoeken die in dezelfde seconde zijn ondertekend nog steeds verschillen. - Het lichaam van een ondertekende verzoek kan maximaal 64 KB zijn, en een lichaam moet
Content-Type: application/json.
Een ontbrekende gebeurtenis komt terug 401 missing_nostr_autheen die slecht gevormd, slecht ondertekend, te oud is, of voor een andere URL, methode of lichaam retourneert
invalid_nostr_auth, met de reden in de boodschap; een hergebruikt een terugkeert
nostr_auth_replayedEen API-sleutel die naar deze eindpunten wordt verzonden, wordt geweigerd.De handtekening wordt gecontroleerd voordat het lichaam wordt gelezen, en elk adres kan het 30 keer per minuut mislukken (een IPv6-adres telt als zijn hele /64); daarna krijgt het 429 met
Retry-After.
Browsers kunnen deze eindpunten alleen oproepen vanaf de eigen sites van Nymbot (https://nymbot.ai,
https://nymchat.app Een pagina op een andere site krijgt geen CORS-headsets terug, dus het kan niet lezen wat ze teruggeven.
OriginZij worden niet beïnvloed.
Ondertekening heeft de geheime sleutel van je nym nodig (de nsecAlleen zet het in een script op een machine die je vertrouwt, lees het uit de omgeving in plaats van het in het bestand te schrijven, en de voorkeur de app wanneer je kunt.
Deze helpers bouwen de kop. De latere voorbeelden op deze pagina gebruiken ze. Ze lezen de geheime sleutel in hex uit NOSTR_SECRET_HEX; de cURL een gebruikt de
Naak command-line tool, die een nsec of hex-sleutel gebruikt, en sha256sum Voor 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");
}
Samenvatting van de rekening
Wat het API-blad van de app bovenaan laat zien: uw openbare sleutel, beide balans, hoeveel sleutels actief zijn (niet herroepen of verlopen), en de Automatische top-up instellingen, of
null Als de server het niet aanbiedt.
GET https://nymbot.ai/api/v1/account nodig a Ondertekend verzoek.
Reactie
{
"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());
Beheren van sleutels
De eindpunten achter de sleutellijst van de app. Ze hebben allemaal een Ondertekend verzoekElke sleutel wordt in deze vorm geretourneerd, met tijden in ISO 8601 en bedragen in sats:
Belangrijkste object
{
"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 Het is voldoende om een sleutel te herkennen, maar niet om deze te gebruiken.De sleutel zelf wordt slechts één keer teruggegeven, wanneer deze is gemaakt.
Lijst van sleutels
GET https://nymbot.ai/api/v1/keys En ondertekend.
| Het veld | type | vereist | Beschrijving |
|---|---|---|---|
include_revoked | Booleaanse taal (query) | niet | Inclusief ingetrokken sleutels. standaard false. |
Reactie
{ "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);
Een sleutel maken
POST https://nymbot.ai/api/v1/keys Aangekondigd. terug 201.
| Het veld | type | vereist | Beschrijving |
|---|---|---|---|
name | String | Ja | 1 tot 40 tekens, anders dan uw andere actieve sleutels (geïgnoreerd geval). |
limit_sats | integer | niet | De uitgavencap in sats, ten minste 1. |
reset_period | String | niet | daily, weekly of monthlyDe behoeften limit_satsLaat het uit voor een hoes die nooit wordt hersteld. |
expire_at | String of integer | niet | Wanneer de sleutel stopt met werken: een ISO 8601-tijd, of milliseconden sinds 1970. |
Beantwoorden (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"
}
}
| Status | Wanneer |
|---|---|
400 | Een ontbrekende of te lange naam; een naam die al in gebruik is (duplicate_name); een cap die niet een geheel getal van ten minste 1 is; een resetperiode zonder cap; een vervaldatum in het verleden; een onbekend veld (unknown_parameter); of 25 actieve sleutels al (too_many_keys). |
429 | Meer dan 60 sleutels gemaakt door deze nimm, of 120 van dit adres, in een uur (rate_limit_exceeded, met 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);
Een sleutel lezen
GET https://nymbot.ai/api/v1/keys/{id} En ondertekend.
terugkeren {"data": {…}} met het belangrijkste object, of 404
key_not_found als geen van uw sleutels die id heeft.
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);
Een sleutel wijzigen
PATCH https://nymbot.ai/api/v1/keys/{id} En ondertekend.
Stuur een van de name, limit_sats, reset_period En
expire_atmet dezelfde regels als bij het maken van een sleutel. null verwijdert een veld: geen cap, geen reset, geen vervaldatum. Het wijzigen van de resetperiode begint een nieuwe periode bij nul. Een herroepte sleutel kan niet worden gewijzigd (400 key_revoked• Terugkeren
{"data": {…}} met het bijgewerkte sleutelobject.
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);
Een sleutel intrekken
DELETE https://nymbot.ai/api/v1/keys/{id} En ondertekend.
Stopt de sleutel meteen, voor goed. Het blijft in de lijst met revoked_at opgesteld, en kan worden gezien met include_revoked=trueAlleen de nieuwste 50 ingetrokken sleutels worden bewaard; de oudere worden verwijderd wanneer een andere sleutel wordt ingetrokken.
Reactie
{ "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());
Nieuwe NWC Auto-top-up
Sluit een Lightning-portemonnee aan met Nostr Wallet Connect en Nymbot bereikt een evenwicht op zichzelf wanneer API-uitgaven het laag laten lopen. Ondertekend verzoek.
Hoe het werkt: nadat een API-aanvraag wordt gechargeerd op het saldo dat u hebt gekozen om te bekijken, als dat saldo onder uw drempel is gevallen, maakt Nymbot een factuur voor uw top-up bedrag, vraagt uw portemonnee om het te betalen en voegt het krediet toe. Het komt maximaal één keer per 5 minuten voor elke nym en saldo, zodat een uitbarsting van verzoeken de portemonnee niet kan leiden. Uitgaven in de apps triggeren het niet. De tijd en grootte van de laatste top-up, en de laatste fout, zijn in de instellingen; als een betaling na een fout ging, controleer de factuur met De top-up status Credit het wel.
Nymbot slaat het versleuteld op en gebruikt het alleen om zijn eigen top-up facturen te betalen, maar maak hiervoor een verbinding, met een uitgavenbudget in je portemonnee, dus het meeste dat het ooit zou kunnen betalen is een nummer dat je hebt gekozen. pay_invoice.
Een portemonnee aansluiten
POST https://nymbot.ai/api/v1/nwc-auto-topup/connect En ondertekend.
| Het veld | type | vereist | Beschrijving |
|---|---|---|---|
nwc_url | String | Ja | De verbinding string, beginnend met nostr+walletconnect://Nymbot vraagt de portemonnee voor get_info voorafgaand aan het opslaan, en opslaat het versleuteld. |
threshold_sats | integer | Ja | Top up wanneer de balans onder deze vele sats valt. |
topup_sats | integer | Ja | Hoeveel om elke keer toe te voegen. 1.000 tot 1.000.000 sats. |
tier | String | niet | pro (de default) of standard: de balans om te kijken en top up. |
Reactie
{
"data": {
"connected": true,
"threshold_sats": 5000,
"topup_sats": 20000,
"tier": "pro",
"last_topup_at": null,
"last_topup_sats": null,
"last_error": null
}
}
| Status | Wanneer |
|---|---|
400 | Geen verbinding met een string (invalid_nwc_url); de portemonnee heeft niet gereageerd op zijn relay (nwc_unreachable) of weigerde de controle (nwc_rejected); de verbinding kan geen facturen betalen (nwc_missing_permission(of een bedrag buiten de limiet. |
501 | Automatische top-ups zijn niet ingeschakeld voor deze server (nwc_unavailableHetzelfde geldt voor de andere twee eindpunten. |
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());
Lees de instellingen
GET https://nymbot.ai/api/v1/nwc-auto-topup En ondertekend.
Retourneert hetzelfde object als het verbinden, met connected: false En de andere velden
null Wanneer er geen portemonnee is aangesloten, wordt de verbindingsreeks zelf nooit teruggestuurd.
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());
ontkoppelen
DELETE https://nymbot.ai/api/v1/nwc-auto-topup/connection En ondertekend.
Verwijdert de opgeslagen verbindingsreeks. Er worden geen meer top-ups gemaakt. Om zeker te zijn, kunt u ook de verbinding in uw portemonnee intrekken.
Reactie
{ "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());
Betalen op aanvraag zonder sleutel
De vaste prijs eindpunten kunnen worden betaald voor één verzoek per keer over Lightning, zonder sleutel, geen account en geen saldo: POST /images/generations, POST /images/edits,
POST /videos, POST /audio/speech, POST /audio/transcriptions,
POST /audio/translations En POST /embeddingsChat, Reacties en berichten hebben altijd een sleutel nodig.Een verzoek dat een sleutel draagt, wordt zoals gewoonlijk in rekening gebracht op het saldo; de betalingsstroom begint alleen als er geen sleutel wordt verzonden.
Nymbot spreekt twee versies van hetzelfde idee, van één backend: Lightning Labs'
L402 (Ook geaccepteerd onder zijn oude naam, LSAT) en het IETF-ontwerp
Betalen HTTP authenticatie schema met de lightning Methode en
charge Gebruik wat je klant begrijpt.
Betalen zonder sleutel is alleen op wanneer API_L402_SECRET bevat ten minste 32 willekeurige bytes, zoals hex (64 tekens) of base64 (44). openssl rand -hex 32Een kortere of vermoedelijke waarde schakelt de functie uit en registreert waarom. API_L402_SECRET_PREVIOUS voor een dag: credentials, status-URL's en uitdagingen die onder het blijven werken tot ze verlopen.
De uitdaging
Stuur het verzoek met nee Authorization header. als het geldig is, loopt niets en je krijgt 402 Payment Required met een factuur voor precies wat dat verzoek kost: dezelfde prijs die een sleutel zou betalen, omgezet op 10 sats een standaard krediet of 100 sats een Pro krediet en afgerond tot een hele sat (ten minste 1 sat, en ten minste de 0.05 credit minimum). WWW-Authenticate Uitdagingen voor dezelfde factuur:
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 }
}
De betaling request De volgende parameters zijn base64url JSON:
{"amount":"237","currency":"sat","methodDetails":{"invoice":"lnbc...","network":"mainnet","paymentHash":"..."}}.
Een uitdaging is gebonden aan het eindpunt, aan de Content-Type (het mediatype en, voor multipart, de limiet) en naar de SHA-256 van het exacte lichaam bytes u verzonden, en duurt 15 minuten. identiek Nogmaals gevraagd: hetzelfde
Content-Type en dezelfde JSON bytes, of voor de multipart eindpunten (/images/edits, /audio/transcriptions, /audio/translationsDe meeste HTTP-bibliotheken kiezen elke keer dat ze een formulier coderen een nieuwe limiet, dus coderen ze het één keer en sturen die bytes twee keer.
Elk adres kan om 30 uitdagingen per minuut vragen (een IPv6-adres telt als zijn hele /64). Verzoeken waarvan het adres niet bekend is delen een strenger 10 per minuut, en er is een algemene drempel op de uitdagingen Nymbot problemen over alle adressen; een verzoek geweigerd voordat een uitdaging wordt gemaakt (bijvoorbeeld met een lichaam dat niet geldig is JSON) telt niet in de richting ervan. 429 met Retry-AfterBetaalde eindpunten die worden gebeld zonder een sleutel of credential tellen ook in op de algemene limiet van 120 niet-geauthenticeerde verzoeken per minuut per adres. Content-Type is niet application/json (of
multipart/form-data voor uploads) wordt geweigerd met 415 En nooit een factuur krijgen.
Embeddings worden geprijsd op basis van een schatting van de tokens in de input, met een margin van 1,5 keer, omdat het werkelijke aantal pas daarna bekend is. Token terugbetalen.
Het verzenden van de betaling
Betaal de factuur met een Lightning portemonnee. De portemonnee geeft u de preimage, 64 hex tekens.
| Schema | header |
|---|---|
| L402 | Authorization: L402 <macaroon>:<preimage> (LSAT Het werkt ook) |
| Betalen | Authorization: Payment <base64url JSON>Waar de JSON is {"challenge": {every parameter of the challenge, as sent}, "payload": {"preimage": "<hex>"}} |
Een betaald verzoek beantwoordt precies zoals een met een sleutel, behalve dat de nymbot
object heeft geen balansvelden: {"payment": "l402", "tier": "pro", "paid_sats": 237,
"charged_sats": 237}En er is geen X-Nymbot-Balance-Sats header. Een verzoek dat is betaald met de betalingssysteem krijgt ook een Payment-Receipt header (base64url JSON met de challenge id, de betaling hash als reference, status En
timestampBetaalde verzoeken zijn niet gekoppeld aan een nim, dus ze verschijnen niet in de querygeschiedenis.
| Status | Wanneer |
|---|---|
402 payment_already_used | Elke betaling betaalt voor één verzoek.Het antwoord is een nieuwe uitdaging voor dit verzoek, dus een klant die zijn laatste credential (zoals lnget Da’s gewoon weer land. |
402 payment_mismatch | De credential werd uitgegeven voor een ander eindpunt, Content-Type of lichaam, of betaalt minder dan dit verzoek nu kost. Een nieuwe uitdaging voor dit verzoek komt ermee mee; als de betaling te klein was, komt wat je betaalde terug als een Token terugbetalen (refund_token En refund_sats in het lichaam). |
402 payment_expired | Meer dan 15 minuten zijn verstreken sinds de uitdaging. er komt een nieuwe uitdaging mee. Als de voorbeeld toont dat je betaald hebt, komt wat je betaald hebt terug als een Token terugbetalen (refund_token En refund_sats in het lichaam), eenmaal; het credential wordt vervolgens gebruikt. |
401 invalid_preimage | De preimage hasht niet op de betalingshash van de factuur. |
401 invalid_payment_credential | De credential is misvormd, is gewijzigd nadat Nymbot het heeft uitgegeven, of namen een betaling hash Nymbot heeft nooit een factuur uitgegeven voor. |
429 rate_limit_exceeded | Meer dan 30 credentials of sleutels die niet konden worden geverifieerd kwamen binnen een minuut van dit adres, of een terugbetaling token werd meer dan 60 keer in een minuut verzonden. 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"));
Klanten zijn gebouwd op mppx met een Lightning-methode omgaan met de Payment-uitdaging zelf; wijzen ze op het eindpunt en laten ze betalen.
Video’s
Een betaald POST /videos Antwoorden 202 als een sleutel één, plus a
status_url: GET Het is ondertekend en werkt 24 uur, zolang de taak wordt bewaard.
{
"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..."
}
}
Houd de refund_token van dit antwoord: het wordt alleen hier weergegeven. Het is leeg terwijl de video weergeeft (GET /api/v1/l402/refunds Antwoorden "status": "pending"); als de rendering niet onbetaald is, landt de betaling daarop.
refund_sats voor een terugbetaalde baan, maar nooit de token, dus het delen van de status-URL deelt de terugbetaling niet.
terugbetalen
Als een betaald verzoek mislukt en de provider Nymbot voor de poging in rekening heeft gebracht, wordt de betaling bewaard en de fout zegt dat, met charged_sats, precies zoals bij een sleutelverzoek. Als het mislukt zonder in rekening te worden gebracht, draagt de fout een Token terugbetalen De moeite waard wat je betaalt:
{
"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"
}
}
Ongebruikte onderdelen komen op dezelfde manier terug: als je twee foto's hebt gevraagd en er een niet is uitgekeerd, is het succes van de reactie nymbot object draagt een terugbetaling token voor de ontbrekende een; een transcriptie waarvan de lengte niet kon worden gelezen vooraf wordt geprijsd voor de langste bestand kan zijn (niet meer dan 30 minuten), en het verschil met de werkelijke lengte komt terug als een terugbetaling token; als het blijkt langer dan 30 minuten, wordt het geweigerd met 413
En de volledige betaling wordt terugbetaald.Inbeddings retourneren wat de schatting vasthield.Een mislukte video wordt terugbetaald naar de token die zijn inzending heeft teruggegeven.
Een restitutietoken is een willekeurige 256-bits code. Nymbot slaat alleen zijn hash op, en het vervalt na 30 dagen.
- Betalen met het. verzenden
Authorization: Bearer REFUND-…Op een van de bovenstaande eindpunten (een OpenAI SDK neemt het als API-sleutel) komt de prijs van het token en blijft er wat overblijft (refund_token_satsIn denymbotEen token die minder waard is dan de vraag beantwoordt402refund_insufficientEen onbetaalde mislukking plaatst de sats terug op dezelfde token. - Check het maar.
GET /api/v1/l402/refundsmet dezelfde header terugkeert{"sats": 237, "status": "open", "expires_at": "..."}. - Een token kan maximaal 60 keer per minuut worden gebruikt.
- Verplaats het naar een nym. Pas het in Verkoop een cadeau in de Nymbot-app, of een oproep
POST /api/v1/l402/refunds/redeemmet a Ondertekend verzoek En{"refund_token": "REFUND-...", "balance": "standard"}(of"pro"Hele credits gaan naar het saldo (10 sats elk op standaard, 100 op Pro); satsen die geen hele credit blijven op de token voor API-verzoeken.
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"
Reactie
{ "data": { "credited": 23, "tier": "standard", "balance_credits": 123, "remaining_sats": 7 } }