Saltu al la enhavo
Reen al Nymbot

Sciobazo Programistoj

Saldo, reŝargoj kaj ŝlosiloj

Kontrolu kion vi havas, plenigu per Lightning, plenigu aŭtomate el via propra monujo, vidu kiom ĉiu peto kostis, kaj administru ŝlosilojn el kodo.

Kontrolante la saldon

Ambaŭ viaj saldoj, kaj kiom de la limo de ĉi tiu ŝlosilo estas uzata.

GET https://nymbot.ai/api/v1/credits/balance — bezonas API-ŝlavenon. POST funkcias ankaŭ, por klientoj kiuj atendas tion.

balance ĉu la du saldoj kune en dolaroj laŭ la nuna Bitcoin-prezo, por iloj kiuj atendas ununumbron (null se la prezo ne povas esti legata). La resto estas en kreditoj kaj sats, kiel la saldoj estas fakte konservataj. key priskribas la ŝlosilon kiun ĝi petis. Ŝlosilo, kiu atingis sian limon, ankoraŭ povas kontroli la saldon.

Respondo

{
  "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"
  }
}
StatoKiam
401La ŝlosilo mankas, estas nekonata, revokita aŭ expirita.

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

Pagmetodoj

Kiel vi povas reŝarĝi, kaj la limoj. Lightning estas la sola metodo.

GET https://nymbot.ai/api/v1/topup/payment-methods — neniu ŝlosilo bezonata.

Plenigo estas de 10 ĝis 1 000 000 sats; Pro-plenigo devas aĉeti almenaŭ unu Pro-krediton, do ĝi komenciĝas je 100 sats. La dolaraj limoj sekvas la Bitcoin-prezon. bulk_bonus listigas la plustonon de kredito ĉe pli grandaj reŝargoj, same kiel en la aplikaĵo: 10%, 15% aŭ 20% pli ĉe normaj reŝargoj de 500, 1,000 aŭ 5,000 sats, kaj ĉe Pro-reŝargoj de 5,000, 10,000 aŭ 50,000 sats.

Respondo

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

Aldonado de kreditो per Lightning

Kreas Lightning-fakturon, kiu aldonas krediton al la nym, al kiu la ŝlosilo apartenas. Pagu ĝin el iu ajn Lightning-portfolio, tiam kontrolu ĝin por havi la krediton aldonitan.

POST https://nymbot.ai/api/v1/topup/create/btc-lightning — bezonas API-ŝlavenon.

KampoSkribiDezirindataPriskribo
amountnumeroJesKiom, en currency. Plena nombro por satoj.
currencyŝnuroNeSATS (la defaŭlto), USD aŭ BTC. Dolároj estas konvertitaj laŭ la nuna Bitcoin-prezo.
tierŝnuroNepro (la defaŭlto) aŭ standard: al kiu saldo la kredito iras.

Standarda kredito estas 10 sats kaj Pro-kredito 100 sats, kune kun iu ajn amasa bonuso; credits diras kion ĉi tiu fakturo aldonos.

Respondo

{
  "invoice_id": "b7d41e0c95a2f38e6c1d0e9a4b7f2c61d3e8a05f9b2c4d7e1a6f3b8c0d5e2a9f4",
  "payment_request": "lnbc100u1p5...",
  "amount_sats": 10000,
  "credits": 115,
  "tier": "pro",
  "expires_at": "2026-09-30T09:27:00Z",
  "status": "pending"
}
StatoKiam
400Alia metodo en la vojo (unsupported_method), nekonata valuto (unsupported_currency) aŭ nivelo, manka kvanto, aŭ kvanto sub la minimumo (amount_too_small), super 1,000,000 sats (amount_too_large) aŭ rifuzita de la Lightning-monoŝlupo (amount_out_of_range).
429Plej ol 60 fakturoj por ĉi tiu nomo, aŭ 120 de ĉi tiu adreso, en unu horo (rate_limit_exceeded, kun Retry-After).
502Neniu fakturo povus esti farita nun (invoice_unavailable, kun 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);

Kontrolante reŝargilon

Demandas ĉu la fakturo estas pagita kaj, post kiam ĝi estas, aldonas la krediton. Kontrolado estas tio, kio kreditas ĝin, do post pagado, kontrolu ĝis la stato estas credited. Re-kontrolado poste estas sekura: la kredito alvenas unufoje, ĉu kiom ajn multajn fojojn vi petas.

GET https://nymbot.ai/api/v1/topup/status/{invoice_id} — bezonas ŝlosilon de la nimo, kiu faris la fakturon.

status estas pending (ankoraŭ ne pagita), paid (pagita, sed ankoraŭ ne kredita; kontrolu denove), credited (sur via saldo) aŭ expired (ne pagita tempovide). La kampoj de saldo estas por la nivelo, kiun la fakturo plenigas.

Respondo

{
  "invoice_id": "b7d41e0c95a2f38e6c1d0e9a4b7f2c61d3e8a05f9b2c4d7e1a6f3b8c0d5e2a9f4",
  "status": "credited",
  "amount_sats": 10000,
  "credits": 115,
  "tier": "pro",
  "expires_at": null,
  "balance_credits": 523.33,
  "balance_sats": 52333
}
StatoKiam
400La identigilo ne estas la 64-karakterlonga identigilo el la krea vokado.
404Neniu fakturo per tiu ID por via pseudonimo (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);

Historio de demandoj

Unu rondo por peto: kio ĝi estis, kiu modelo, kiom da tokenoj kaj kio ĝi kostis. Neniuj promptoj aŭ respondoj estas konservataj, do neniu estas redonita. Rondoj estas konservataj dum 90 tagoj, plej novaj unuaj. Klavo kiu atingis sian limon ankoraŭ povas legi sian historion.

GET https://nymbot.ai/api/v1/queries/history — bezonas API-ŝlosilon, kiu vidas siajn proprajn petojn, aŭ subskribita peto el via nomo, kiu vidas ĉiun ŝlosilon.

KampoSkribiDezirindataPriskribo
pageentiero (kvrido)NeDefaŭlto 1, maksimume 1,000; pli alta paĝo estas 400 invalid_value.
page_countentiero (kvrido)NeLinioj por paĝo. Defaŭlte 20, maksimume 100.
start_date
end_date
ĉeno (demando)NeISO 8601 datoj aŭ tempoj.
modelĉeno (demando)NeNur ĉi tiu modelo.
typeĉeno (demando)Nechat, responses, messages, image, video, speech, transcription aŭ embedding.
all_keysbooleana (demando)NeKun ŝlosilo: true inkluzivas ĉiun ŝlosilon de la sama sufikso. Defaalto false.
key_idĉeno (demando)NePer subskribita peto, aŭ per all_keys=true: nur ĉi tiu ŝlosilo.

Respondo

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

Subskribado de kontaj petoj

Krei, ŝanĝi kaj revoki ŝlosilojn, la kontosummunon kaj aŭtomatajn plenigojn ne postulas API-ŝlosilon. Ili postulas subskribon de via nym, do fuŝita ŝlosilo povas elspezi ĝis sian limon, sed neniam povas krei alian ŝlosilon aŭ levni sian propran limon.

La aplikaĵo faras tion por vi: ĉion en ĝia API tabulo uzas ĉi tiujn finpunktojn. Vi nur bezonas ĉi tiun sekcion por administri ŝlosilojn el via propra kodo.

La subskribo estas Nostr-evento de speco 27235 (NIP-98), sendita base64-ŝifrite en la Authorization ĉeftitolo kun la vorto Nostr antaŭe:

La evento

{
  "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 ĉu la plena URL de la peto, inkluzive de la demando-ĉeno, estas tute tiel kiel sendita.
  • method estas la HTTP-metodo.
  • payload estas la SHA-256 de la ruĝa peto-korpo, en hex. Ĝi estas postulata en POST kaj PATCH, kaj la korpo kiun vi sendas devas esti bajto post bajto la unu kiun vi haseis.
  • created_at devas esti ene de 60 sekundoj de la horloĝo de la servilo.
  • Ĉiu evento funkcias unufoje, a GET inkluzivite, do kaptita ĉefliko ne povas esti reŝaltita. Subskribu novan por ĉiu peto. Aldonu ĵaŭ nonce etendo kun hazardvaloro tiel du petoj subskribitaj en la sama sekundo ankoraŭ diferencas.
  • La korpo de subskribita peto povas esti maksimume 64 KB, kaj korpo bezonas Content-Type: application/json.

Mankanta evento revenas 401 missing_nostr_auth; unu kiu estas malbone formita, malbone subskribita, tro malnova, aŭ por malsama URL, metodo aŭ korpo revenas invalid_nostr_auth, kun la kialo en la mesaĝo; reuzita revenas nostr_auth_replayed. API-ŝlosilo sendita al tiuj estponoj estas malakceptita. La subskribo estas kontrolita antaŭ ol la korpo estas legita, kaj ĉiu adreso povas malsukcesi pri ĝi 30 fojojn minute (IPv6-adreso kalkulas kiel sia tuta /64); post tio ĝi ricevas 429 kun Retry-After.

Retumiloj povas voki tiujn finpunktojn nur el la propraj retejoj de Nymbot (https://nymbot.ai, https://nymchat.app kaj iliaj subdomenoj). Paĝo sur iu ajn alia retejo ne ricevas CORS intestarojn, do ĝi ne povas legi tion, kion ili redonas. Skriptoj kaj denaskaj aplikaĵoj, kiuj ne sendas Origin, ne estas afektitaj.

Via sekreta ŝlosilo

Subskribado bezonas la sekretan ŝlosilon de via nimo (la nsec), kiu kontrolas ĉion: vian identecon, vian historion kaj vian saldon. Metu ĝin nur en skripton en maŝino, kiun vi fidas, legu ĝin el la medio anstataŭ skribi ĝin en la dosieron, kaj preferu la aplikaĵon kiam vi povas.

Tiuj helpiloj konstruas la ĉeflinion. La postaĵaj ekzemploj sur ĉi tiu paĝo uzas ilin. Ili legas la sekretan ŝlosilon en heksadecimala formo el NOSTR_SECRET_HEX; la cURL-ia uzas la nak komandlinia ilo, kiu akceptas nsec- aŭ hex- ŝlosilon, kaj sha256sum (sur 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");
}

La kontsumo de la konto

Kion la API-foldeEstas de la apliko montras ĉe la supro: via publika ŝlosilo, ambaŭ saldoj, kiom da ŝlosiloj estas aktivaj (ne revokitaj aŭ expirintaj), kaj la aŭtomata reŝargado agordoj, aŭ null kiam la servilo ne proponas ilin.

GET https://nymbot.ai/api/v1/account — bezonas subskribita peto.

Respondo

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

Administrado de ŝlosiloj

La finepunktoj malantaŭ la ŝlosillisto de la aplikaĵo. Ili ĉiuj bezonas subskribita peto. Ĉiu ŝlosilo estas redonita en ĉi tiu formo, kun tempoj en ISO 8601 kaj montoj en sats:

Ŝlosila objekto

{
  "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 sufiĉas por rekoni ŝlosilon sed ne por uzi ĝin. La ŝlosilo mem estas redonata nur unufoje, kiam ĝi estas farita.

Listigo de ŝlosiloj

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

KampoSkribiDezirindataPriskribo
include_revokedbooleana (demando)NeInkluzivu revokitajn ŝlosilojn. Defaŭlto false.

Respondo

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

Fabrikado de ŝlosilo

POST https://nymbot.ai/api/v1/keys — subskribita. Returoj 201.

KampoSkribiDezirindataPriskribo
nameŝnuroJesDe 1 ĝis 40 karakteroj, malsama de viaj aliaj aktivaj ŝlosiloj (ignorante la distingon inter majuskloj kaj malgrandaj literoj).
limit_satsentiera nombroNeLa elspezlimo en sats, almenaŭ 1. Lasu ĝin for por sen limigo.
reset_periodŝnuroNedaily, weekly aŭ monthly. Bezonoj limit_sats. Lasu ĝin eksteren por ĉapelo, kiu neniam reaseta.
expire_atĉeno aŭ entieroNeKiam la ŝlosilo ĉesas funkcii: ISO 8601 tempo, aŭ milisekundoj ekde 1970.

Respondo (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"
  }
}
StatoKiam
400Mankanta aŭ tro longa nomo; nomo jam uzata (duplicate_name); limigo, kiu ne estas tuta nombro de almenaŭ 1; nuliga periodo sen limigo; eksvalidigo en la pasinteco; nekonata kampo (unknown_parameter); aŭ jam 25 aktivaj ŝlosiloj (too_many_keys).
429Plej ol 60 ŝlosiloj faritaj de ĉi tiu nimeco, aŭ 120 de ĉi tiu adreso, en unu horo (rate_limit_exceeded, kun 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);

Legi unu ŝlosilon

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

Revenoj {"data": {…}} per la ŝlosila objekto, aŭ 404 key_not_found se neniu de viaj ŝlosiloj havas tiun identigilon.

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

ŝanĝi ŝlosilon

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

Sendu ajen de name, limit_sats, reset_period kaj expire_at, kun la samaj reguloj kiel kiam farante ŝlosilon. null malplenigas kampon: sen limigo, sen reinitialigo, sen eksvalidigo. Ŝanĝi la reinitialigan periodon komencas novan periodon ĉe nulo. Revokita ŝlosilo ne povas esti ŝanĝita (400 key_revoked). Revenoj {"data": {…}} kun la ĝisdatigita ŝlosila objekto.

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

Revokado de ŝlosilo

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

Haltenas la ŝlosilon tuj, por ĉiam. Ĝi restas en la listo kun revoked_at fiksita, kaj povas esti vidata kun include_revoked=true. Revoki ŝlosilon, kiu jam estas revokita, respondas laŭ la sama maniero. Nur la plej novaj 50 revokitaj ŝlosiloj estas konservataj; pli malnovajoj estas fordelataj kiam alia ŝlosilo estas revokita.

Respondo

{ "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 aŭtomata plenigo

Konektu Lightning-portofolon per Nostr Wallet Connect kaj Nymbot memstare plenigas saldon kiam API-elspezoj malpliigas ĝin. La API-tabulo de la aplikaĵo havas la samajn agordojn; ĉi tiuj estas la terminalpunktoj malantaŭ ĝi. Ĉiuj de ili bezonas a subskribita peto.

Kiel ĝi funkcias: post kiam API-peto estas elŝutita el la saldo, kiun vi elektis por spuri, se tiu saldo falis sub vian limvaloron, Nymbot faras fakturon por via pleniga sumo, petas vian monofonon pagi ĝin, kaj aldonas la krediton. Ĝi plenigas plej multiveke unufoje ĉiun 5 minuton por ĉiu nym kaj saldo, do fulmveto da petoj ne povas elĉuigi la monofonon. Elspezoj en la apoj ne provokas ĝin. La tempo kaj grandeco de la lasta plenigo, kaj la lasta eraro, estas en la agordoj; se pagado sukcesis post eraro, kontroli ĝian fakturon per la stato de la reaplikuĝo kreditas ĝin.

Antaŭ ol vi konektas portfolion

Konekstra stringo permesas al iu ajn, kiu posedas ĝin, peti vian portfolion pagi. Nymbot konservas ĝin ĉifrite kaj ĉiam uzas ĝin nur por pagi siajn propriajn plenigon-fakturojn, sed kreu konekton nur por tio, kun elspezbudjeto en via portfolio, por ke la plej multa, kion ĝi iam povus pagi, estu nombro, kiun vi elektis. La portfolio devas subteni pay_invoice.

Konekti portfolion

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

KampoSkribiDezirindataPriskribo
nwc_urlŝnuroJesLa konekstraĵo, komencante nostr+walletconnect://. Nymbot petas la portfolion pri get_info antaŭ konservado de ĝi, kaj konservas ĝin ĉifrite.
threshold_satsentiera nombroJesŜarĝu kiam la saldo falas sub ĉi tiom da sats. Almenaŭ 1,000.
topup_satsentiera nombroJesKiom aldonu ĉiumerke. 1,000 ĝis 1,000,000 sats.
tierŝnuroNepro (la defaŭlto) aŭ standard: la saldo por viglidi kaj reŝargi.

Respondo

{
  "data": {
    "connected": true,
    "threshold_sats": 5000,
    "topup_sats": 20000,
    "tier": "pro",
    "last_topup_at": null,
    "last_topup_sats": null,
    "last_error": null
  }
}
StatoKiam
400Ne konekstra ĉeno (invalid_nwc_url); la portfolio ne respondis per sia rellegio (nwc_unreachable) aŭ rifuzis la ĉekon (nwc_rejected); la konekto ne povas pagi fakturojn (nwc_missing_permission); aŭ kvanto ekster la limoj.
501Aŭtomataj plenigas ne estas aktivigitaj por ĉi tiu servilo (nwc_unavailable). La sama aplikiĝas al la aliaj du finiĝpunktoj.

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

Legante la agordojn

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

Redonas la saman objekton kiel koneki, kun connected: false kaj la aliaj kampoj null kiam neniu portfolio estas konektita. La konektiga ĉeno mem neniam estas revenigita.

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

Mal konektiĝante

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

Forvortigas la konservitan konekstrigon. Neniuj plenigoj plu estos farataj. Por esti certa, vi ankaŭ povas revoki la konekton en via portmoneo.

Respondo

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

Pagado po peto sen ŝlosilo

La fiksprezaj defrenĉoj povas esti pagitaj por unu peto foje tuj per Lightning, sen ŝlosilo, sen konto kaj sen saldo: POST /images/generations, POST /images/edits, POST /videos, POST /audio/speech, POST /audio/transcriptions, POST /audio/translations kaj POST /embeddings. Babiloj, Respondoj kaj Mesagoj ĉiam bezonas ŝlosilon. Peto, kiu portas ŝlosilon, estas fakturita al la saldo kiel kutime; la pagfluo nur komenciĝas kiam neniu ŝlosilo estas sendita.

Nymbot parolas du versiojn de la sama ideo, el unu backend: Lightning Labs' L402 (ankaŭ akceptita sub sia malnova nomo, LSAT) kaj la IETF-ŝarpon Pagado HTTP-aŭtentikiga skemo kun la lightning metodo kaj charge intenco. Uzu tion, kiun via kliento komprenas.

Ruliĝi vian propran servilon

Pagado sen ŝlosilo estas aktiva nur kiam API_L402_SECRET tenas almenaŭ 32 hasardajn bajtojn, kiel hex (64 karakteroj) aŭ base64 (44). Faru unu kun openssl rand -hex 32. Mallonga aŭ atributebla valoro malŝaltas la funkcion kaj registras kial. Por rotigi ĝin, movu la malnovan valoron al API_L402_SECRET_PREVIOUS dum unu tago: kredencialoj, status-URL-oj kaj defioj faritaj sub ĝi daŭre funkcias ĝis ili ekspiras.

La defio

Sendu la peton sen neniu Authorization intesto. Se ĝi estas valida, nenio funkcias, kaj vi ricevas 402 Payment Required kun fakturo por precize tio, kion tiu peto kostas: la sama prezo, kiun ŝlosilo pagus, konvertita al 10 satoj por standarda kredito aŭ 100 satoj por Pro-kredito kaj rondigita supraŭ al tuta sato (almenaŭ 1 sato, kaj almenaŭ la 0,05-kredita minimumo). La respondo portas tri WWW-Authenticate defioj por la sama fakturo:

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

La pago request parametro estas base64url JSON: {"amount":"237","currency":"sat","methodDetails":{"invoice":"lnbc...","network":"mainnet","paymentHash":"..."}}.

Defio estas ligita al la fino, al la Content-Type (Ĝia mediatipo kaj, por multipart, ĝia limo) kaj al la SHA-256 de la precizaj korpaj bajtoj kiujn vi sendis, kaj daŭras 15 minutojn. Post pagado, sendu la identika petu denove: la sama Content-Type kaj la samaj JSON-bajtoj, aŭ por la multipart-unuopunktoj (/images/edits, /audio/transcriptions, /audio/translations) la saman multipart-korpon kun la sama limo. Plej multaj HTTP-bibliotekoj elektas novan limon ĉiu foje kiam ili kodas formularon, do kodu ĝin unufoje kaj sendu tiujn bajtojn dufoje.

Ĉiu adreso povas peti 30 defiojn je minuto (IPv6-adreso okazas kiel sia tuta /64). Petoj, kies adreso ne estas konata, dividas pli striktan limigon de 10 je minuto, kaj ekzistas ĝenerala supra limo de la defioj, kiujn Nymbot eldonas tra ĉiuj adresoj; peto malakceptita antaŭ ol defio estas farita (ekzemple per korpo, kiu ne estas valida JSON) ne kalkulas al ĝi. Preter tio la respondo estas 429 kun Retry-After, sendita antaŭ ol la korpo estas legata. Pagitaj punktoj vokitaj sen ŝlosilo aŭ kredencialo ankaŭ kalkulas al la ĝenerala limo de 120 neautentikitaj petoj po minuto por adreso. Kredencialo estas kontrolita antaŭ ol la korpo estas legata, kaj peto kies Content-Type ne estas application/json (aŭ multipart/form-data por ŝarĝoj) estas rifuzata per 415 kaj neniam ricevas fakturon.

Embedoj estas prezigitaj laŭ estimato de la tokenoj en la enigo, kun 1.5× margeno, ĉar la reala nombro estas konata nur poste. Vi pagas por la tokenoj efektive uzataj, kaj la neuzata parto de la pago revenas kiel bonpaga jetono.

Sendante la pagon

Pagu la fakturon per iu ajn Lightning-portfolio. La portfolio donas al vi la preimagenon, 64 heksadecimajn karakterojn. Tiam sendu la saman peton per unu el ĉi tiuj:

로도Headero
L402Authorization: L402 <macaroon>:<preimage> (LSAT funkcias ankaŭ)
PagadoAuthorization: Payment <base64url JSON>, kie estas la JSON {"challenge": {every parameter of the challenge, as sent}, "payload": {"preimage": "<hex>"}}

Pagita peto respondas tute same kiel unu farita per ŝlosilo, krom ke la nymbot objekto ne havas balansajn kampojn: {"payment": "l402", "tier": "pro", "paid_sats": 237, "charged_sats": 237}, kaj ne estas X-Nymbot-Balance-Sats kapo. Peto pagita per la pagskemo ankaŭ ricevas Payment-Receipt header (base64url JSON kun la defio-id, la pag-hasto kiel reference, status kaj timestamp). Pagitaj petoj ne estas ligitaj al iu ajn nimo, do ili ne aperas en la kverio-historio.

StatoKiam
402 payment_already_usedĈiu pagado pagas por unu peto. La respondo estas freŝa defio por ĉi tiu peto, do kliento, kiu kaŝas sian lastan kredencialon (kiel lnget simple vereなje pagas denove.
402 payment_mismatchLa kredencialo estis eldonita por alia fino, Content-Type a korpo, aŭ pagas malpli ol ĉi tiu peto nun kostas. Nova defio por ĉi tiu peto venas kun ĝi; se la pago estis tro malgranda, tio kion vi pagis revenas kiel bonpaga jetono (refund_token kaj refund_sats en la korpo).
402 payment_expiredPlej ol 15 minutoj pasis ekde la defio. Nova defio venas kun ĝi. Se la antaŭbildo montras, ke vi pagis, tio kion vi pagis revenas kiel bonpaga jetono (refund_token kaj refund_sats en la korpo), unufoje; la kredencaĵo tiam estas eluzita.
401 invalid_preimageLa antaŭbildo ne haseas al la pag-hasiĝo de la fakturo. La pago ne estas eluzita.
401 invalid_payment_credentialLa kredencialo estas malformita, estis ŝanĝita post kiam Nymbot eldonis ĝin, aŭ nomas pag-hason por kiu Nymbot neniam eldonis fakturon. Makarono kun limigo kiun Nymbot ne konas, aŭ kun kontradiktaj limigoj, estas rifuzita.
429 rate_limit_exceededPlej ol 30 kredenciais aŭ ŝlosiloj, kiuj malsukcesis verigi, venis de ĉi tiu adreso en unu minuto, aŭ unu rinfunda ŝlosilo estis sendita pli ol 60 fojojn en unu minuto. Atendu por 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"));

Klientoj konstruitaj sur mppx per Lightning-metodo traktu la pagan ĉalendon mem; direktu ilin al la fino kaj lasu ilin pagi.

Videoj

Pagita POST /videos respondoj 202 simila al ŝlosila, plus a status_url: GET ĝi sen iu ajn ŝlosilo por sekvi la laboron. Ĝi estas subskribita kaj funkcias dum 24 horoj, tiel longe kiel la laboro estas konservita.

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

Konservu la refund_token el tiu respondo: ĝi estas montrita nur ĉi tie. Ĝi estas malplena dum la video rindeas (GET /api/v1/l402/refunds respondoj "status": "pending"); se la rendo malsukcesas nefakture, la pago landas sur ĝi. La statuso URL montras refund_sats por reredita tasko sed neniam la tokenon, do dividi la status-URL ne dividas la rerediton. Kontroli la tokenon ankaŭ finas ne sukcesan taskon, kiun neniu kontrolis.

Refundoj

Se pagita peto malsukcesas kaj la provizanto fakturis Nymbot por la provo, la pagon estas konservata kaj la eraro diras tion, kun charged_sats, precize kiel por ŝlosila peto. Se ĝi malsukcesas sen esti fakturigita, la eraro portas bonpaga jetono valoras tion, kion vi pagis:

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

Neuzataj partoj revenas en la sama maniero: se vi petis du bildojn kaj unu malsukcesis ne fakturite, la sukcesa respondo's nymbot objekto portas refund-tokenon por la mankaĵo; transkriptaĵo, kies longon ne povis esti legita antaŭe, estas priskribita laŭ la plej longa ebla longo de la dosiero (neniam pli ol 30 minutoj), kaj la diferenco al la reala longo revenas kiel refund-tokeno; se ĝi okazas esti pli longa ol 30 minutoj, ĝi estas rifuzata kun 413 kaj la tuta pagado revenas. Embedingoj redonas tion, kion la estimado retenis. Malfelcita video refundigas al la jetono, kiun ĝia submeto redonis.

Refund-token estas hazarda 256-bita kodo. Nymbot konservas nur ĝian haŝon, kaj ĝi eksvalidiĝas post 30 tagoj. Ĝi enhavas sat-saldon, kaj vi povas:

  • Pagu per ĝi. Sendi Authorization: Bearer REFUND-… sur iu ajn de la fino-punktoj supre (OpenAI SDK prenas ĝin kiel sian API-ŝlosilon). La prezo venas el la tokeno kaj tio, kio restas, restas sur ĝi (refund_token_sats en la nymbot objekto). Tokeno valora malpli ol la peto respondas 402 refund_insufficient; nefakturita malsukceso metas la satŝojn reen sur la saman tokenon.
  • Kontrolu ĝin. GET /api/v1/l402/refunds kun la sama ĉeftitolo rezultas {"sats": 237, "status": "open", "expires_at": "..."}.
  • Unu tokeno povas esti uzata plej multfoje 60 fojojn ene de unu minuto.
  • Movu ĝin al nimo. Alglui ĝin en Redemu donacon en la Nymbot-aplikaĵo, aŭ telefoni POST /api/v1/l402/refunds/redeem kun unu subskribita peto kaj {"refund_token": "REFUND-...", "balance": "standard"} (aŭ "pro"). La tutaj kreditoj iras al la saldo (10 sats ĉium de standard, 100 sur Pro); sats kiuj ne formis plenan krediton restas sur la token por API petoj.

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"

Respondo

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