Skip ad content
Reditus ad Nymbot

Scientia basis Developers

Solvendum, additamenta et claves

Vide quod habes, imple per Lightning, imple automatisme ex proprio portu, vide quidquid quaestio constat, et claves ex codice ger.

Saldum inspiciens

Utraque saldi tuae, et quanto huius clavis capitis usurpați sunt.

GET https://nymbot.ai/api/v1/credits/balance — clavis API indiget. POST Etiam operatur, clientibus qui id exspectant.

balance suntne duo saldia simul in dollariis pretio Bitcoin praesenti, pro instrumentis quae unum numerum expectant (null si pretium legi non potest). Reliquum est in creditis et sats, qua modo saldi re vera conservantur. key Clavem quae petivit describit. Clavis quae finem suum attigit, tamen libram inspicere potest.

Responsio

{
  "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"
  }
}
StatusQuando
401Clavis deest, incognita, abrogata aut expirata est.

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 solutionis

Quomodo creditum addere possis, et limites. Lightning est sola ratio.

GET https://nymbot.ai/api/v1/topup/payment-methods — clavis non opus est.

Iniectio est ab 10 ad 1,000,000 sats; iniectio Pro debet saltem unum creditum Pro emere, ergo incipit ab 100 sats. Limites dollari secundum pretium Bitcoin sequuntur. bulk_bonus creditum additum in maioribus increscentibus enumerat, idem sicut in applicatione: 10%, 15% vel 20% plus in ordinariis increscentibus ex 500, 1,000 vel 5,000 sats, et in Pro increscentibus ex 5,000, 10,000 vel 50,000 sats.

Responsio

{
  "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);

Incredere per Lightning

Facturam Lightning facit quae creditum addit nomini cui clavis pertinet. Ex quacumque portu Lightning eam solva, tunc... probatum vide ut creditum addatur.

POST https://nymbot.ai/api/v1/topup/create/btc-lightning — clavis API indiget.

CampusTypusNecessariumDescriptio
amountnumerusItaQuantum, in currencyNumerus integer pro sats.
currencyfilumNonSATS (defaultum), USD aut BTC. Dollarae ex praetio praesenti Bitcoin convertuntur.
tierfilumNonpro (defaultum) an standard: cui saldo creditum pervenit.

Crēditum ordinārium est 10 sats et crēditum Pro 100 sats, praeter aliquem praemium emptionis in magnā quantitā; credits dicit quid haec factura addere debeat.

Responsio

{
  "invoice_id": "b7d41e0c95a2f38e6c1d0e9a4b7f2c61d3e8a05f9b2c4d7e1a6f3b8c0d5e2a9f4",
  "payment_request": "lnbc100u1p5...",
  "amount_sats": 10000,
  "credits": 115,
  "tier": "pro",
  "expires_at": "2026-09-30T09:27:00Z",
  "status": "pending"
}
StatusQuando
400Aliter modus in itinere (unsupported_method), numisma incognitum (unsupported_currency) aut gradus, numerus deest, aut numerus sub minimo (amount_too_small), supra 1,000,000 sats (amount_too_large) an ab portu fulgurante recusatum (amount_out_of_range).
429Plus quam sexaginta rationes huius nominis, aut centum viginti ex hoc domicilio, intra horam (*rate_limit_exceeded, cum Retry-After).
502Nullo tempore nunc facturae iniectio potest (invoice_unavailable, cum 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);

Incrémentum praestandum investigans

Rogat an factura soluta sit et, cum soluta erit, creditum addit. Investigatio est quae id credit, itaque post solvendum, investiga donec status sit credited. Iterum inspicere postea tutum est: creditum semel accidit, quamvisIamcum petas.

GET https://nymbot.ai/api/v1/topup/status/{invoice_id} — claves ab nomine qui rationem reddidit opus habet.

status est pending (nondum solutum), paid (solutum, sed nondum creditum; iterum inspice), credited (in saldo tuo) an expired (non tempestive solutum). Campi saldos sunt pro gradu cuius factura auget.

Responsio

{
  "invoice_id": "b7d41e0c95a2f38e6c1d0e9a4b7f2c61d3e8a05f9b2c4d7e1a6f3b8c0d5e2a9f4",
  "status": "credited",
  "amount_sats": 10000,
  "credits": 115,
  "tier": "pro",
  "expires_at": null,
  "balance_credits": 523.33,
  "balance_sats": 52333
}
StatusQuando
400Id non est id sexaginta quattuor litterarum ex vocatione creationis.
404Nulla factura cum eo nomine pro eo nomine tuo ✉️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);

Historia interrogationum

Una linea per postulationem: quid erat, quae mensura, quot numeri verborum et quid constat. Ne prompta aut responsa servantur, neque reddantur. Lineae per nonaginta dies servantur, novissima primum. Clavis quae finem attigit historiam suam tamen legere potest.

GET https://nymbot.ai/api/v1/queries/history — claves API indiget, quae suas ipsas peticiones videt, aut petitio signata Ex nomine tuo, quod omnis clavis videt.

CampusTypusNecessariumDescriptio
pagenumerus integer (quaestio)NonDefault 1, maxime 1,000; pagina altius est 400 invalid_value.
page_countnumerus integer (quaestio)NonSeries per paginam. Default 20, maxime 100.
start_date
end_date
filum (quaestio)NonTempora vel dies secundum normam ISO 8601.
modelfilum (quaestio)NonSolum hoc exemplar.
typefilum (quaestio)Nonchat, responses, messages, image, video, speech, transcription aut embedding.
all_keysboolean (quaestio)NonCum clave: true omnes claves eiviusdem nominis includit. Default false.
key_idfilum (quaestio)NonCum petitione signata, vel cum all_keys=true: haec sola clavis.

Responsio

{
  "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);

Petitiones rationum signandae

Claves faciendi, mutandi et revocandi, summa rationis et replenishmentis automatici claves API non accipiunt. Signaturam ex nym vestro accipiunt, itaque clavis effusa usque ad limitem suum adhiberi potest, sed numquam aliam clavem facere vel proprium limitem augere potest.

App haec tibi hoc facit: omnia in loco suo API tabula hos fines utitur. Hanc tantum partem tibi opus est ad claves ex tuo codice regendi.

Signatura est eventus Nostr genus 27235 (NIP-98), in base64 codicibus missus in Authorization caput cum verbo Nostr ante

Eventus

{
  "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": "..."
}
  • u estne URL integra petitionis, etiam nexus interrogationis inclusa, plane sicut missa.
  • method methodus HTTP est.
  • payload est SHA-256 corporis petendi purissimi, in hex. Opus est in POST et PATCH, et corpus quod mittis debet esse byte pro byte idem quem hashisti.
  • created_at intra sexaginta secunda horologii servi intra esse debet.
  • Omnis eventus semel operatur, a GET inclusum, ita ut caput captum non possit reperiari. Signum novum pro omni postulatione adde. nonce index cum valore fortuito signa ut duo petitiones eodem secundo signatae tamen differant.
  • Corpus rogationis signatae vix 64 KB esse potest, et corpus opus est Content-Type: application/json.

Eventus amissus redit 401 missing_nostr_auth; quod male constructum, male signatum, nimis vetustum, aut ad aliam URL, methodum, vel corpus, reddit invalid_nostr_auth, cum ratione in nuntio; res reused redit nostr_auth_replayed. Clavis API ad has fines mitto recusatur. Signatura antequam corpus legatur probatur, et omnis locus potest id triginta vices per minutam deficere (adhaerens adnotationem IPv6 tuta numeratur ut tota /64); postea capit 429 cum Retry-After.

Navigatores hos fines tantum ex ipsis situs Nymbot vocare possunt (https://nymbot.ai, https://nymchat.app (et subdominia eorum). Pagina in ulla alia pagina nullo CORS headeribus redditur, ne ea quae reddunt legere possit. Scripta et applicationes nativae, quae nihil mittunt Origin, non afficiuntur.

Clavis tua secreta

Signatio clave secretam pseudonymi tui requirit (id... nsec), quod omnia regit: identitatem tuam, historiam tuam et numerum tuum. Sive in scripto in machina quam fidas pone, e medio ambientis lege potius quam in文件中 scribe, et applicationem praefer si potes.

Hi these adiutores caput aedificant. Exempla posteriora in hac pagina ea utuntur. Clavis secreta in hexus leguntur ex NOSTR_SECRET_HEX; cURL unum utitur nihil instrumentum lineae imperii, quod clavim nsec vel hex accipit, et sha256sum (in 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");
}

Summarium rationis

Quod tabula API applicationis in summo ostendit: clavem tuam publicam, utrumque saldo, quot claves sint activae (non abrogatae vel expiratas), et... repletio automatica configuratio, an null cum servitor eis non praebet.

GET https://nymbot.ai/api/v1/account — opus est petitio signata.

Responsio

{
  "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());

Claves administrando

Terminatio ante listam clavis applicationis. Omnes eae opus habent signatum rogatum. Omnis clavis in hâ formâ redditur, temporibus in ISO 8601 et quantitatibus in sats:

Objectum clavem

{
  "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 satis est clavem agnoscere sed non uti. Ipsum clavis solum semel reddeatur, cum fiat.

Claves enumeratio

GET https://nymbot.ai/api/v1/keys — subscriptum.

CampusTypusNecessariumDescriptio
include_revokedboolean (quaestio)NonClāvēs abrogātās inclūde. Defáltum. false.

Responsio

{ "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);

Clavem faciens

POST https://nymbot.ai/api/v1/keys — subscriptum. Reditus 201.

CampusTypusNecessariumDescriptio
namefilumItaaI 1 usque ad 40 litterae, diversae a aliis clāvibus tuis activis (non spectantibus casu).
limit_satsintegerNonLimites impensarum in sats, saltem 1. Sine eo si nullum limitis est.
reset_periodfilumNondaily, weekly aut monthly. Egestatibus limit_satsRelinque id pro caluto quae numquam renovatur.
expire_atseries an numerus integerNonCum clavis cessat operari: tempus ISO 8601, vel milliseconda ab anno 1970.

Responsum (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"
  }
}
StatusQuando
400Nomen deest vel nimis longum; nomen iam in usu (duplicate_name); limitatio quae non est numerus integer saltem 1; periodus renovationis sine limite; expiratio in praeterito; campus ignotus (unknown_parameter); vel 25 clavis activae iam (too_many_keys).
429Plus quam sexaginta claves ab hoc nomine factae, aut centum viginti ex hac inscriptione, intra horam (rate_limit_exceeded, cum 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);

Legens unum clavis

GET https://nymbot.ai/api/v1/keys/{id} — subscriptum.

Reditus {"data": {…}} cum obiecto clave, aut 404 key_not_found si nemo clavis tuae illud numerum habet.

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);

Clavem mutare

PATCH https://nymbot.ai/api/v1/keys/{id} — subscriptum.

Mitte aliquid ex name, limit_sats, reset_period et expire_at, eodem regulis sicut clavem faciendo. null sine limite, sine reformatio, sine expiratione. Mutatio periodi refractionis novum periodum a zero incipit. Clavis revocata mutari non potest (400 key_revoked). Redde {"data": {…}} cum obiecto clavis renovato.

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);

Clavem revocare

DELETE https://nymbot.ai/api/v1/keys/{id} — subscriptum.

Clavet iam statim, in perpetuum. In indice manet cum revoked_at positum, et videri potest cum include_revoked=trueClavem iam revocatam revocare eodem modo respondet. Solum quinquaginta novissima claves revocatae servantur; vetustiora deleuntur cum altera clavis revocetur.

Responsio

{ "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());

NWC automaticus repletus

Porticulum Lightning cum Nostr Wallet Connect iunge, et Nymbot numerum ipse implet cum sumptus API eum minuant. Tabula API applicationis eadem configuratione habet; haec sunt puncta terminantia quae eam regunt. Omnia eorum opus est petitio signata.

Quomodo operatur: postquam postulatum API ex saldo quod vigilare elegisti debitum est, si hoc saldo sub limitem tuum cecidit, Nymbot rationem reddendam pro quantitate muneris addendi facit, portulam tuam ut id solvat rogat, et creditum addit. Quinquaginta minutis timor maxime semel pro quoque nym et saldo munus addit, ut impetu postulationum portula exhauriari non possit. In applicationibus expensio hoc non incitat. Tempus et magnitudinem ultimi muneris addendi, et ultimam errorem, in numeris inveniri; si solutio post errorem peracta est, rationem eius per... status creditionis hoc credit.

Antequam porticulum conectas

Series connectionis quemcumque eum qui eam tenet tibi permittit ut portulum tuum solvat. Nymbot eam encryptam servat et tantum ad suas ipsas sumptus facturas solvendas utitur, sed fac nexum tantum ad hoc, cum pecunia limitata in portulo tuo, ut maximum quod ever solvat sit numerus quem elegisti. Portulum hoc sustinere debet pay_invoice.

Marsupium coniungere

POST https://nymbot.ai/api/v1/nwc-auto-topup/connect — subscriptum.

CampusTypusNecessariumDescriptio
nwc_urlfilumItaSeries connectionis, incipiens nostr+walletconnect://. Nymbot portulum petit pro get_info antequam id servet, et id encryptum conservat.
threshold_satsintegerItaImple cum saldus infra hoc numero satorum descendit. Saltem 1,000.
topup_satsintegerItaQuantum addere oportet singulis vicibus. 1,000 ad 1,000,000 sats.
tierfilumNonpro (defaultum) an standard: saldo spectandi et implegendi.

Responsio

{
  "data": {
    "connected": true,
    "threshold_sats": 5000,
    "topup_sats": 20000,
    "tier": "pro",
    "last_topup_at": null,
    "last_topup_sats": null,
    "last_error": null
  }
}
StatusQuando
400Non est catena connectionis (invalid_nwc_url); portulus per suum relay non respondit (nwc_unreachable) an examinatio recusavit (nwc_rejected); connexio libellos pecuniarios solvere non potest (nwc_missing_permission); vel summa extra limites.
501Incredibilia supplementa ad hoc servum non sunt activata (nwc_unavailable). Id idem ad alia duo puncta terminalia spectat.

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());

Configurationes legens

GET https://nymbot.ai/api/v1/nwc-auto-topup — subscriptum.

Eodem obiecto redit cum nectit, cum connected: false et cetera officia null cum nullum malum (wallet) iunctum sit. Ipsa series connectionis numquam reditur.

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());

Disconnectens

DELETE https://nymbot.ai/api/v1/nwc-auto-topup/connection — subscriptum.

Delet nectere stringem conservatum. Nulla amplificatio iam facitur. Ut certe sis, nectere etiam in portulente tuo revocare potes.

Responsio

{ "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());

Solvendum per petitionem sine clave

Termini pretii fixi per Lightning pro unaque postulatione tempore solvi possunt, sine clave, sine ratione et sine saldo: POST /images/generations, POST /images/edits, POST /videos, POST /audio/speech, POST /audio/transcriptions, POST /audio/translations et POST /embeddingsColloquia, responsiones, et nuntii semper clavem requirunt. Petitio quae clavem portat sicut consuetum est ex saldo computatur; processus solutionis tantum incipit cum nulla clavis mittaatur.

Nymbot duas eadem sententiam dicere, ex uno backend: Lightning Labs' L402 (etiam sub nomine veteri accipitur, LSAT) et propositum IETF Solutio Ratio authenticationis HTTP cum lightning ratio et charge Propositum. Utere eo quod cliens tuus intellegit.

Proprium servitium tuum regere

Solutio sine clave tantum fit cum... API_L402_SECRET continet saltem 32 bytes fortuito, ut hex (64 characteres) vel base64 (44). Fac unum cum openssl rand -hex 32. Valor brevior vel praedicibilis hoc munus desactivat et cur id fiat nuntiat. Ut eum vertas, antiquum valorem move ad API_L402_SECRET_PREVIOUS pro die: credentia, URL status et difficultates sub eo factae operari continuent donec expirant.

Certamen

Mitte petitionem sine Authorization caput. Si validum est, nihil currit, et obtienes 402 Payment Required cum factura pro eo exacto quod illa postulatio constat: eadem pretio quod clavis solveret, conversum decem sats pro credito ordinario vel centum sats pro credito Pro, et ad satum integrum elevatum (saltem unum sat, et minimum creditum 0.05). Responsum tria fert. WWW-Authenticate difficultates pro eadem factura:

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 }
}

Solutio request parameter est base64url JSON: {"amount":"237","currency":"sat","methodDetails":{"invoice":"lnbc...","network":"mainnet","paymentHash":"..."}}.

Certamen ad finem, ad... obligatum est Content-Type (genus mediae et, pro multipartibus, finis eius) et ad SHA-256 eorumque corporum byte exactorum quae misisti, et per XV minuta durat. Post solvendum, mitte similis iterum peto: idem Content-Type et eodem JSON byte, vel pro punctis terminis multipartibus (/images/edits, /audio/transcriptions, /audio/translations) eadem corpus multipartium eademque limite. Maximae bibliothecae HTTP novum finem (boundary) eligunt cumque formam codificant, ergo semel codifica et illos bytes bis mitte.

Omnis inscriptio potest petere trentes provocationes per minutam (inscriptio IPv6 ut tota /64 numeratur). Rogationes quorum inscriptio ignota est decem per minutam, quae severior est, communem habent, et est limitatio generalis in provocationibus quas Nymbot omnibus inscriptionibus mittit; rogatio quae repulsa est antequam provocatio fiat (exempli gratia cum corpore quod non est JSON validum) ad id non numeratur. Ultra hoc responsum est 429 cum Retry-After, antequam corpus legatur mittitur. Termini finitos sine clavi vel notae appellatae etiam in numerum generalem centum viginti postulati per minutam per unum locum sine auctoritate computantur. Notam antequam corpus legatur probat, et postulatum cuius Content-Type non est application/json (vel multipart/form-data pro uploadis) cum recusatur 415 et numquam recipit rationem.

Infixiones pretio aestimationis numerorum in introductio estimati, cum margine 1.5×, quoniam numerus verus postea tantum cognoscitur. Pro numeris re vera usatis numeras solvis, et pars incommutata solutionis redit ut signum reditionis.

Solutio mittitur

Solve expeditionem cum quacumque portulā Lightning. Portula tibi praeimaginem dat, sexaginta quattuor characteres hexadecimales. Deinde eundem petitionem mitte cum uno horum:

ConsiliumCaput
L402Authorization: L402 <macaroon>:<preimage> (LSAT etiam operatur)
SolutioAuthorization: Payment <base64url JSON>, ubi JSON est {"challenge": {every parameter of the challenge, as sent}, "payload": {"preimage": "<hex>"}}

Petitio soluta responsum exacte sicut illa quae clave facta est praebet, nisiquam... nymbot Objectum campos aequilibrii non habet: {"payment": "l402", "tier": "pro", "paid_sats": 237, "charged_sats": 237}, et non est X-Nymbot-Balance-Sats caput. A petitio cum Ratione Solutionis soluta etiam habet Payment-Receipt caput (base64url JSON cum numero identificationis provocationis, cum hash solutionis ut reference, status et timestamp). Rogationes solutae ad ullum nomen non nexa sunt, ita ut in historia quaestionum non appareant.

StatusQuando
402 payment_already_usedOmnis solutio pro uno postulatione solvit. Responsum est novum provocatio pro hac postulatione, ergo emptor qui novissimam auctoritatem servat (ut lnget simpliciter denuo solvit.
402 payment_mismatchNotitia ad aliud finem emissa est, Content-Type aut corpus, aut minus quam hoc postulatum nunc constat. Novum provocatio hoc postulatum cum eo venit; si solutio nimis parva erat, quod solvis revertitur ut signum reditionis (refund_token et refund_sats in corpore).
402 payment_expiredPlus quam XV minuta ab provocatione praeterita. Nova provocatio cum eo venit. Si praeimago monstrat te pendisse, id quod pendisti redit ut signum reditionis (refund_token et refund_sats in corpore), semel; titulus tunc consumptus est.
401 invalid_preimagePraeimago ad summationem solutionis facturae non convertitur. Sol
401 invalid_payment_credentialPraepostitium male formatum est, mutatum est postquam Nymbot id emisit, vel numerum solutionis nominat cuius Nymbot numquam rationem emisit. Macaroon cum condicione quam Nymbot non novit, vel cum condicionibus conflictibus, recusatur.
429 rate_limit_exceededUltra triginta signa vel claves quae vericanda non potuerunt ex hac addressio intra minutam orta sunt, aut unum signum restitutio intra minutam plus quam sexaginta vicibus missum est. Expecta 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"));

Clientes in structi mppx Per methodum Lightning provocationem solutionis ipsi superent; ad finem dirige eos et solvere sin.

Videa

Solutus POST /videos responsa 202 ut unus clavatus, etcum status_url: GET sine ulla clave ut opus sequatur. Signatum est et viget per XXIV horas, dum opus servatur.

{
  "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..."
  }
}

Serva refund_token ex hoc responso: solum hic ostenditur. Inane est dum video formatur (GET /api/v1/l402/refunds responsa "status": "pending"); si deductio sine numeris facta deficit, solutio ei incidit. Status URL ostendit refund_sats pro opere restituto sed numquam sigillo, itaque status URL non restitutionem communicat. Sigillum quoque examinare operem deficiente qui nemo investigavit componit.

Redributiones

Si postulatio soluta deficit et praebitor Nymbotem propter conatum monetavit, solutio retinetur et error hoc indicat, cum charged_sats, plane sicut pro rogatu signato. Si deficit sine pecunia exigenda, error errorem fert signum reditionis pretium quod solvi:

{
  "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"
  }
}

Partes inutiles eodem modo revertuntur: si duas imagines petiveris et una sine pecunia debita defecerit, responsum successus nymbot res obiectum momentum redimensionis pro eo quod deest ferit; transcriptio cuius longitudo antea legi non potuit pretium pro maxima longitudini quam fieri potest (nunquam plus quam triginta minuta) habet, et differentia ad veram longitudinem ut momentum redimensionis redit; si maior quam triginta minuta evaderit, cum hoc recusatur 413 et totum solutio redit. Embeddings reddeunt quod aestimatio retinebat. Video deficiente redditur ei signo quod submissio eius reddidit.

Signum reditus est codex aleatorius 256-bit. Nymbot tantum eius hash conservat, et post 30 dies expiravit. Saldo sat continet, et potes:

  • Eisce cum eo. Mitte Authorization: Bearer REFUND-… in quibuscumque punctorum supra dictorum (OpenAI SDK id ut clavis API accipit). Pretium ex token deductum est et quod reliquum manet in eo (refund_token_sats in nymbot objectum). Signum pretio minoris postulati respondet 402 refund_insufficient; defectus non computatus satoshis in eundem token revertit.
  • Examina id. GET /api/v1/l402/refunds eadem capite redit {"sats": 237, "status": "open", "expires_at": "..."}.
  • Unum signum intra minutum septuaginta vicibus saltem adhiberi potest.
  • Ad nomen anonymum move. Inscribe id in Donum redime in app Nymbot, aut chiama POST /api/v1/l402/refunds/redeem cum petitio signata et {"refund_token": "REFUND-...", "balance": "standard"} (vel "pro"). Omnes credita ad saldo pertinunt (10 sats unumque in standard, 100 in Pro); sats quae integrum creditum non faciunt in signo remanent pro requistibus 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"

Responsio

{ "data": { "credited": 23, "tier": "standard", "balance_credits": 123, "remaining_sats": 7 } }