Slaan oor na die inhoud
Terug na Nymbot

Kennisbasis Ontwikkelaar

Balans, top-ups en sleutels

Kyk wat jy het, top-up oor Lightning, top-up outomaties van jou eie portemonnee, sien wat elke versoek kos, en bestuur sleutels uit kode.

Kyk na die balans

Beide van jou salarisse, en hoeveel van hierdie sleutel se cap gebruik word.

GET https://nymbot.ai/api/v1/credits/balance Jy het 'n API sleutel nodig. POST Dit werk ook vir kliënte wat dit verwag.

balance is die twee salde saam in dollars by die huidige Bitcoin prys, vir gereedskap wat 'n enkele getal verwag (null Die res is in krediete en sats, wat is hoe die salde eintlik gehou word. key 'N sleutel wat sy cap bereik het, kan steeds die balans kyk.

Reaksies

{
  "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"
  }
}
StatusWanneer
401Die sleutel is ontbreek, onbekend, herroep of verval.

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

Betaalmetodes

Hoe jy kan top-up, en die limiete. Lightning is die enigste metode.

GET https://nymbot.ai/api/v1/topup/payment-methods Geen sleutel nodig nie.

'N Top-up is 10 tot 1.000.000 sats; 'n Pro-top-up moet ten minste een Pro-krediet koop, dus dit begin by 100 sats. bulk_bonus die ekstra krediet op groter top-ups, dieselfde as in die 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.

Reaksies

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

Opstaan oor die bliksem

Maak 'n Lightning-rekening wat krediet byvoeg aan die nym waaraan die sleutel behoort. Kyk dit Om die krediet toe te voeg.

POST https://nymbot.ai/api/v1/topup/create/btc-lightning Jy het 'n API sleutel nodig.

Die veldDie tipevereisBeskrywing
amountDie nommerJaHoeveel, in currency'N hele getal vir die sats.
currencyStringnieSATS die vervalsing, USD of BTCDollars word omskep by die huidige Bitcoin prys.
tierStringniepro (die verval) of standard: watter balans die krediet gaan.

'N Standaard krediet is 10 sats en 'n Pro krediet 100 sats, plus enige bulk bonus; credits sê wat hierdie rekening sal byvoeg.

Reaksies

{
  "invoice_id": "b7d41e0c95a2f38e6c1d0e9a4b7f2c61d3e8a05f9b2c4d7e1a6f3b8c0d5e2a9f4",
  "payment_request": "lnbc100u1p5...",
  "amount_sats": 10000,
  "credits": 115,
  "tier": "pro",
  "expires_at": "2026-09-30T09:27:00Z",
  "status": "pending"
}
StatusWanneer
400Nog 'n metode in die pad (unsupported_method, 'n onbekende geldeenheid (unsupported_currency) of tier, 'n ontbrekende bedrag, of 'n bedrag onder die minimum (amount_too_smallMeer as 1 000 000 (amount_too_large) of verwerp deur die Lightning portemonnee (amount_out_of_range).
429Meer as 60 fakture vir hierdie nym, of 120 van hierdie adres, in 'n uur (rate_limit_exceeded, met Retry-After).
502Geen rekening kan op die oomblik gemaak word nie (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);

Kyk na 'n top-up

Vra of die rekening betaal is en, sodra dit het, voeg die krediet by. Check is wat dit krediet, so na betaling, kyk totdat die status is creditedKyk daarna weer is veilig: die krediet land een keer, maak nie saak hoeveel keer jy vra nie.

GET https://nymbot.ai/api/v1/topup/status/{invoice_id} - benodig 'n sleutel van die nym wat die faktuur gemaak het.

status is pending (Ek het nog nie betaal nie) paid (Betal, maar nog nie gekrediteer nie; kyk weer), credited (op jou balans) of expired (nie tydelik betaal nie). Die balansvelde is vir die vlak waarop die faktuur boonste is.

Reaksies

{
  "invoice_id": "b7d41e0c95a2f38e6c1d0e9a4b7f2c61d3e8a05f9b2c4d7e1a6f3b8c0d5e2a9f4",
  "status": "credited",
  "amount_sats": 10000,
  "credits": 115,
  "tier": "pro",
  "expires_at": null,
  "balance_credits": 523.33,
  "balance_sats": 52333
}
StatusWanneer
400Die id is nie die 64-karakter id van die skep oproep nie.
404Geen rekening deur daardie id vir jou 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);

Historie wil hê

Een rigting per versoek: wat dit was, watter model, hoeveel tokens en wat dit kos. Geen oproepe of antwoorde word bewaar nie, sodat geen teruggekeer word nie. Rigtings word vir 90 dae bewaar, die nuutste eerste. 'N sleutel wat sy cap bereik het, kan nog steeds sy geskiedenis lees.

GET https://nymbot.ai/api/v1/queries/history — benodig 'n API sleutel, wat sy eie versoekings sien, of 'n Onderteken verzoek van jou nim, wat elke sleutel sien.

Die veldDie tipevereisBeskrywing
pageIntegrasie van die vraag (query)nieStandaard 1, ten hoogste 1000; 'n hoër bladsy is 400 invalid_value.
page_countIntegrasie van die vraag (query)nieRooi per bladsy. Standaard 20, met 'n maksimum van 100.
start_date
end_date
Die vraag (query)nieISO 8601 datum of tyd.
modelDie vraag (query)nieNet hierdie model.
typeDie vraag (query)niechat, responses, messages, image, video, speech, transcription of embedding.
all_keysDie Booleaanse (Query)nieMet ’n sleutel: true bevat elke sleutel van dieselfde nym. false.
key_idDie vraag (query)nieMet 'n ondertekende versoek, of met all_keys=trueAlleen die sleutel.

Reaksies

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

Handtekening van rekeningverzoeke

Maak, verander en herroep sleutels, die rekeningopsomming en outomatiese top-ups neem nie 'n API sleutel nie. Hulle neem 'n handtekening van jou nym, sodat 'n lekkie sleutel tot sy cap kan spandeer, maar kan nooit 'n ander sleutel maak of sy eie cap verhoog nie.

Die app doen dit vir jou: alles in sy vuur die sheet gebruik hierdie eindpunte. Jy benodig hierdie afdeling net om sleutels uit jou eie kode te bestuur.

Die handtekening is 'n Nostr-gebeurtenis van soort 27235 (NIP-98), gestuur base64-gekodeer in die Authorization Header met die woord Nostr In die voorkant:

Die gebeurtenis

{
  "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 is die volledige URL van die versoek, query string ingesluit, presies soos gestuur.
  • method Dit is die HTTP metode.
  • payload is die SHA-256 van die ruwe versoek lichaam, in hex. POST en PATCH, en die liggaam wat jy stuur, moet byte vir byte wees wat jy hash het.
  • created_at Dit moet binne 60 sekondes van die bediener se klok wees.
  • Elke gebeurtenis werk een keer, a GET ingesluit, sodat 'n opgeneem kop nie weer gespeel kan word nie. Sluit 'n nuwe kop vir elke versoek. Voeg 'n nonce tag met 'n ewekansige waarde sodat twee versoekings wat in dieselfde sekonde onderteken is, steeds verskillend is.
  • Die liggaam van 'n ondertekente versoek kan maksimum 64 KB wees, en 'n liggaam benodig Content-Type: application/json.

'n Vermiste gebeurtenis terugkeer 401 missing_nostr_auth; een wat slegs gevorm is, slegs onderteken, te oud, of vir 'n ander URL, metode of liggaam retourneer invalid_nostr_authmet die rede in die boodskap; 'n hergebruikte een terugkeer nostr_auth_replayed'N API sleutel wat na hierdie eindpunte gestuur word, word geweier.Die handtekening word gekeken voordat die liggaam gelees word, en elke adres kan dit 30 keer per minuut misluk ('n IPv6 adres tel as sy hele /64); daarna kry dit 429 met Retry-After.

Browsers kan hierdie eindpunte slegs van Nymbot se eie webwerwe oproep (https://nymbot.ai, https://nymchat.app 'N bladsy op enige ander webwerf kry geen CORS-kopies terug nie, sodat dit nie kan lees wat hulle terugkeer nie. OriginHulle word nie beïnvloed nie.

Jou geheime sleutel

Die handtekening benodig jou nym se geheime sleutel (die nsec), wat alles beheer: jou identiteit, jou geskiedenis en jou balans. Sluit dit net in 'n script op 'n masjien wat jy vertrou, lees dit uit die omgewing eerder as om dit in die lêer te skryf, en verkies die app wanneer jy kan.

Hierdie helpers bou die kop. Die latere voorbeelde op hierdie bladsy gebruik hulle. Hulle lees die geheime sleutel in hex van NOSTR_SECRET_HEX; die cURL een gebruik die Naak 'n kommando-lyntool, wat 'n nsec of hex sleutel neem, en sha256sum op die 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");
}

Die rekeningversameling

Wat die app se API-blad aan die top wys: jou openbare sleutel, beide balans, hoeveel sleutels aktief is (nie herroep of verval nie), en die Automatiese top-up instellings, of null wanneer die bediener hulle nie bied nie.

GET https://nymbot.ai/api/v1/account Die behoefte a Onderteken verzoek.

Reaksies

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

Die bestuur van sleutels

Die eindpunte agter die app se sleutel lys. Almal van hulle benodig 'n Onderteken verzoekElke sleutel word in hierdie vorm teruggegee, met tye in ISO 8601 en bedrag in sats:

Belangrike voorwerp

{
  "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 Dit is genoeg om 'n sleutel te herken, maar nie om dit te gebruik nie.

Skryf sleutels

GET https://nymbot.ai/api/v1/keys Ek het onderteken.

Die veldDie tipevereisBeskrywing
include_revokedDie Booleaanse (Query)nieInclusief herroepde sleutels. standaard false.

Reaksies

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

Maak 'n sleutel

POST https://nymbot.ai/api/v1/keys Geskryf, terugkeer 201.

Die veldDie tipevereisBeskrywing
nameStringJa1 tot 40 karakters, anders as jou ander aktiewe sleutels (ignoreer geval).
limit_satsIntegraalnieDie besteding cap in sats, ten minste 1.
reset_periodStringniedaily, weekly of monthlyDie behoeftes limit_satsLaat dit uit vir 'n hoed wat nooit herstel nie.
expire_atString of integernieWanneer die sleutel ophou werk: 'n ISO 8601 tyd, of millisekondes sedert 1970.

Die antwoord (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"
  }
}
StatusWanneer
400'N Ontbrekende of te lang naam; 'n naam wat reeds gebruik word (duplicate_name); 'n cap wat nie 'n hele getal van ten minste 1 is nie; 'n herstelperiode sonder cap; 'n vervaldatum in die verlede; 'n onbekende veld (unknown_parameter); of 25 aktiewe sleutels reeds (too_many_keys).
429Meer as 60 sleutels gemaak deur hierdie nim, of 120 van hierdie adres, in 'n 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);

Lees 'n sleutel

GET https://nymbot.ai/api/v1/keys/{id} Ek het onderteken.

terugkeer {"data": {…}} met die sleutel voorwerp, of 404 key_not_found as geen sleutel van jou die ID het nie.

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

Verander 'n sleutel

PATCH https://nymbot.ai/api/v1/keys/{id} Ek het onderteken.

Stuur enige name, limit_sats, reset_period en expire_at, met dieselfde reëls as wanneer jy 'n sleutel maak. null 'n veld verwyder: geen cap, geen reset, geen vervaldatum nie. Die verandering van die resetperiode begin 'n nuwe tydperk by nul. 'n herroepde sleutel kan nie verander word nie (400 key_revoked• Terugkeer {"data": {…}} met die opgedateerde sleutel voorwerp.

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

Verwyder 'n sleutel

DELETE https://nymbot.ai/api/v1/keys/{id} Ek het onderteken.

Stop die sleutel dadelik, vir goed. Dit bly in die lys met revoked_at geskep, en kan gesien word met include_revoked=trueDie herroeping van 'n sleutel wat reeds herroep is, antwoord op dieselfde manier.Slegs die nuutste 50 herroepte sleutels word bewaar; ouer sleutels word verwyder wanneer 'n ander sleutel herroep word.

Reaksies

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

Die NWC-auto-top-up

Sluit 'n Lightning-beursie met Nostr Wallet Connect en Nymbot sal 'n balans op hulleself opstel wanneer API-uitgawes dit laai. Onderteken verzoek.

Hoe dit werk: Nadat 'n API-verzoek op die saldo gekry word wat jy gekies het om te kyk, as daardie saldo onder jou drempel gedaal het, maak Nymbot 'n faktuur vir jou top-up bedrag, vra jou portemonnee om dit te betaal, en voeg die krediet by. Dit klop maximaal een keer elke 5 minute vir elke nym en saldo, sodat 'n uitbarsting van versoekings die portemonnee nie kan verdraai nie. Spandeer in die programme veroorsaak dit nie. Die tyd en grootte van die laaste top-up, en die laaste fout, is in die instellings; as 'n betaling na 'n fout gegaan het, kyk sy faktuur met Die top-up status krediet daarvoor.

Voordat jy 'n portemonnee verbind

Nymbot stoor dit versleuteld en gebruik dit slegs om sy eie top-up fakture te betaal, maar maak 'n verbinding net vir hierdie, met 'n uitgawesbegroting in jou portemonnee, sodat die meeste wat dit ooit kon betaal, is 'n nommer wat jy gekies het. pay_invoice.

Die verbinding van 'n portemonnee

POST https://nymbot.ai/api/v1/nwc-auto-topup/connect Ek het onderteken.

Die veldDie tipevereisBeskrywing
nwc_urlStringJaDie verbinding string, begin nostr+walletconnect://Nymbot vra die portemonnee vir get_info Voordat jy dit opslaan, en stoor dit versleutel.
threshold_satsIntegraalJaTop op wanneer die balans onder hierdie baie sats val. Ten minste 1000.
topup_satsIntegraalJaHoeveel om elke keer by te voeg. 1000 tot 1.000.000 sats.
tierStringniepro (die verval) of standard: die balans om te kyk en top op.

Reaksies

{
  "data": {
    "connected": true,
    "threshold_sats": 5000,
    "topup_sats": 20000,
    "tier": "pro",
    "last_topup_at": null,
    "last_topup_sats": null,
    "last_error": null
  }
}
StatusWanneer
400Nie 'n koppelvlak nie (invalid_nwc_url(Hulle het nie ’n antwoord op die boodskap nie.)nwc_unreachable) of verwerp die toets (nwc_rejected); die verbinding kan nie rekeninge betaal nie (nwc_missing_permission); of 'n bedrag buite die limiete.
501Dit is nie nodig om die rekenaar te verwyder nie (nwc_unavailableDieselfde geld vir die ander twee eindpunte.

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 die instellings

GET https://nymbot.ai/api/v1/nwc-auto-topup Ek het onderteken.

Gee dieselfde voorwerp as die verbinding, met connected: false En die ander velde null wanneer geen portemonnee verbind word nie. Die verbindingsreeks self word nooit teruggestuur nie.

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

Ontkoppeling

DELETE https://nymbot.ai/api/v1/nwc-auto-topup/connection Ek het onderteken.

Verwyder die opgeslagen verbindingsreeks. Geen meer top-ups word gemaak nie. Om seker te wees, kan jy ook die verbinding in jou portemonnee herroep.

Reaksies

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

Betaal per versoek sonder sleutel

Die vaste-prys eindpunte kan betaal word vir een versoek per tyd oor Lightning, sonder sleutel, geen rekening en geen saldo: POST /images/generations, POST /images/edits, POST /videos, POST /audio/speech, POST /audio/transcriptions, POST /audio/translations en POST /embeddingsChat, antwoorde en boodskappe benodig altyd 'n sleutel. 'N Verzoek wat 'n sleutel dra, word soos gewoonlik in rekening geneem; die betalingstroom begin slegs wanneer geen sleutel gestuur word nie.

Nymbot praat twee weergawes van dieselfde idee, van een backend: Lightning Labs' Die L402 (Ook aanvaar onder sy ou naam, LSAT(en die IETF ontwerp Betaling HTTP-authentification skema met die lightning Die metode en charge Gebruik wat jou kliënt verstaan.

Maak jou eie server

Betaal sonder 'n sleutel is slegs aan wanneer API_L402_SECRET bevat ten minste 32 ewekansige bytes, soos hex (64 karakters) of base64 (44). openssl rand -hex 32'N Korter of vermoedbare waarde sluit die funksie uit en log waarom. API_L402_SECRET_PREVIOUS vir 'n dag: credentials, status URL's en uitdagings wat onder dit gemaak word, werk totdat hulle verval.

Die uitdaging

Stuur die versoek met nie Authorization header. As dit geldig is, loop niks, en jy kry 402 Payment Required met 'n faktuur vir presies wat die versoek koste: dieselfde prys wat 'n sleutel sou betaal, omskep by 10 sats 'n standaard krediet of 100 sats 'n Pro krediet en afgerond tot 'n hele sat (ten minste 1 sat, en ten minste 0.05 krediet minimum). WWW-Authenticate uitdagings vir dieselfde rekening:

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

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

'N Uitdaging is gebind aan die eindpunt, aan die Content-Type (sy media tipe en, vir multipart, sy grens) en na die SHA-256 van die presiese liggaam bytes wat jy gestuur het, en duur 15 minute. Identiese Nog 'n vraag: dieselfde Content-Type en dieselfde JSON bytes, of vir die multipart eindpunte (/images/edits, /audio/transcriptions, /audio/translationsDie meeste HTTP biblioteke kies 'n nuwe grens elke keer as hulle 'n vorm kodeer, so kodeer dit een keer en stuur daardie bytes twee keer.

Elke adres kan vra vir 30 uitdagings per minuut ('n IPv6 adres tel as sy hele /64). Versoekings wie se adres nie bekend is deel 'n strenger 10 per minuut, en daar is 'n algehele cap op die uitdagings Nymbot probleme oor al die adresse; 'n versoek verwerp voordat 'n uitdaging gemaak word (byvoorbeeld met 'n liggaam wat nie geldig is JSON) tel nie teenoor dit. 429 met Retry-AfterBetaalde eindpunte wat sonder 'n sleutel of credential geroep word, tel ook tot die algemene limiet van 120 nie-authenticeerde versoekings per minuut per adres. Content-Type is nie application/json (en die multipart/form-data vir oplaai) word verwerp met 415 en nooit 'n rekening ontvang nie.

Embeddings word geprijs op grond van 'n raming van die tokens in die invoer, met 'n 1.5× marge, aangesien die werklike getal slegs daarna bekend is. Herstel die token.

Stuur die betaling

Betaal die rekening met enige Lightning-beursie. Die beursie gee jou die voorbeeld, 64 hex karakters.

SkemaDie header
Die L402Authorization: L402 <macaroon>:<preimage> (LSAT Ek werk ook)
BetalingAuthorization: Payment <base64url JSON>waar die JSON is {"challenge": {every parameter of the challenge, as sent}, "payload": {"preimage": "<hex>"}}

'N Betaalde versoek beantwoord presies soos een wat met 'n sleutel gedoen word, behalwe dat die nymbot Object het geen balansvelde nie: {"payment": "l402", "tier": "pro", "paid_sats": 237, "charged_sats": 237}En daar is geen X-Nymbot-Balance-Sats 'N Verzoek wat met die betalingstelsel betaal word, kry ook 'n Payment-Receipt header (base64url JSON met die uitdaging id, die betaling hash as reference, status en timestampBetaalde versoekings is nie gekoppel aan enige nym nie, dus verskyn hulle nie in die query geskiedenis nie.

StatusWanneer
402 payment_already_usedElke betaling betaal vir een versoek. Die antwoord is 'n nuwe uitdaging vir hierdie versoek, so 'n kliënt wat sy laaste credential (soos lnget Dit is gewoonlik weer land.
402 payment_mismatchDie credential is uitgereik vir 'n ander eindpunt, Content-Type of liggaam, of betaal minder as hierdie versoek nou kos. 'n nuwe uitdaging vir hierdie versoek kom saam met dit; as die betaling te klein was, wat jy betaal, kom terug as 'n Herstel die token (refund_token en refund_sats in die liggaam).
402 payment_expiredMeer as 15 minute het verby gegaan sedert die uitdaging. 'n Nuwe uitdaging kom saam met dit. As die voorbeeld wys dat jy betaal het, kom wat jy betaal het terug as 'n Herstel die token (refund_token en refund_sats in die liggaam), een keer; die credential word dan uitgeput.
401 invalid_preimageDie voorbeeld haas nie op die betaling haas van die faktuur nie.
401 invalid_payment_credentialDie credential is verkeerd gevorm, is verander nadat Nymbot dit uitgereik het, of name 'n betaling hash Nymbot het nooit 'n faktuur uitgereik nie. 'N macaroon met 'n waarschuwing Nymbot weet nie, of met teenstrydige waarschuwings, word geweier.
429 rate_limit_exceededMeer as 30 credentials of sleutels wat misluk het om te verifieer, het in 'n minuut van hierdie adres gekom, of een terugbetaling token is meer as 60 keer in 'n minuut gestuur. 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"));

Kliënte gebou op mppx met 'n Lightning metode die betaling uitdaging self hanteer; wys hulle op die eindpunt en laat hulle betaal.

Video’s

Die betaalde POST /videos Antwoorde 202 soos 'n sleutel een, plus 'n status_url: GET dit sonder enige sleutel om die werk te volg. Dit is onderteken en werk vir 24 uur, solank die werk gehou word.

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

Hou die refund_token Dit is nie die enigste ding wat jy kan doen nie, want die video is leeg (GET /api/v1/l402/refunds Antwoorde "status": "pending"); as die render misluk nie, word die betaling daarop geland. refund_sats vir 'n terugbetaalde werk, maar nooit die token nie, dus deel die status URL deel nie die terugbetaling nie.

Vergoeding

As 'n betaalde versoek misluk en die verskaffer Nymbot vir die poging in rekening geneem het, word die betaling gehou en die fout sê so, met charged_sats, presies soos vir 'n sleutelverzoek. As dit misluk sonder dat dit in rekening geneem word, dra die fout 'n Herstel die token Wat jy betaal:

{
  "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 dele kom op dieselfde manier terug: as jy twee foto's gevra het en een mislukte onbetaal, is die sukses van die reaksie nymbot die voorwerp dra 'n terugbetaling token vir die ontbrekende een; 'n transkripsie waarvan die lengte nie voor kan lees word geprys vir die langste die lêer kan wees (nie meer as 30 minute), en die verskil tot die werklike lengte kom terug as 'n terugbetaling token; as dit blyk te wees langer as 30 minute, word dit geweier met 413 en die hele betaling kom terug. Embeddings terug wat die raming teruggehou het. 'n mislukte video terugbetaal die token wat sy inskrywing teruggekeer het.

'N terugbetaling token is 'n ewekansige 256-bits kode. Nymbot stoor slegs sy hash, en dit verval na 30 dae.

  • Betaal daarmee. Stuur Authorization: Bearer REFUND-… Dit is 'n verskil tussen die oorspronklike oorspronklike oorspronklike oorspronklike oorspronklike oorspronklike oorspronklike oorsprong (refund_token_sats In die nymbot 'N Token wat minder waard is as wat die versoek beantwoord 402 refund_insufficient'N Onbetaalde mislukking plaas die sats terug op dieselfde token.
  • Controleer dit. GET /api/v1/l402/refunds met dieselfde header terugkeer {"sats": 237, "status": "open", "expires_at": "..."}.
  • Een token kan maximaal 60 keer per minuut gebruik word.
  • Beweeg dit na 'n nym. Pas dit in Maak 'n geskenk in die Nymbot-toepassing, of bel POST /api/v1/l402/refunds/redeem met a Onderteken verzoek en {"refund_token": "REFUND-...", "balance": "standard"} (en die "pro"Die hele krediete gaan na die balans (10 sats elkeen op standaard, 100 op Pro); sats wat nie 'n hele krediet bly op die token vir API-versoek nie.

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"

Reaksies

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