Gean nei de ynhâld
Komme nei Nymbot.

Kennisbasis De ûntwikkelers

Balans, top-ups en keys

Check wat jo hawwe, top up oer Lightning, top up automatysk út jo eigen wallet, sjoch wat elke fraach kostet, en beheare sleutels út koade.

Kontrolearje de balans

Beide fan jo balanzen, en hoeveel fan dizze sleutel's cap wurdt brûkt.

GET https://nymbot.ai/api/v1/credits/balance Der is gjin fire key nedich. POST It wurket ek foar klanten dy't it ferwachtsje.

balance is de twa saldi yn dollars by de hjoeddeistige Bitcoin-priis, foar tools dy't in ienige tal ferwachtsje (null De rest is yn credits en sats, dat is hoe't de saldo's wer hâlden wurde. key In sleutel dy't har cap berikt, kin noch kontrolearje it saldo.

Antwurd

{
  "balance": 49.18,
  "balance_sats": 42037,
  "standard": { "credits": 120.4, "sats": 1204 },
  "pro": { "credits": 408.33, "sats": 40833 },
  "key": {
    "id": "4f0c9a1be27d3856",
    "name": "laptop scripts",
    "limit_sats": 20000,
    "period_used_sats": 3412,
    "total_used_sats": 18230,
    "reset_period": "monthly",
    "reset_at": "2026-10-01T00:00:00Z"
  }
}
Status fanAs
401De sleutel is mislearre, onbekend, revokearre of ferlitte.

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

Betalingsmetoaden

Hoe kinne jo top up, en de limieten. Lightning is de ienige metoade.

GET https://nymbot.ai/api/v1/topup/payment-methods De sleutel is net nedich.

In top-up is 10 oant 1.000.000 sats; in Pro-top-up hat te keapjen op syn minst ien Pro-krediet, dus it begjint by 100 sats. bulk_bonus List de ekstra kredyt op gruttere top-ups, itselde as yn de app: 10%, 15% of 20% mear op standert top-ups fan 500, 1000 of 5000 satsen, en op Pro top-ups fan 5.000, 10.000 of 50.000 satsen.

Antwurd

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

It giet oer Lightning.

Makket in Lightning-factura dy't kredyt oan de nym dy't de sleutel belutsen is. Betelje it út elke Lightning-wallet, dan Kontrolearje it Om de kredyt oan te kommen.

POST https://nymbot.ai/api/v1/topup/create/btc-lightning Der is gjin fire key nedich.

De fjildDe typeNeffensBeskriuwing
amountNûmerJaHoeveel, yn currencyIn hele tal foar sats.
currencyStrjitteNo ynSATS Yn de ûndersteande USD Of BTCDollars wurde konvertearre oan de hjoeddeistige Bitcoin-priis.
tierStrjitteNo ynpro (De foarbyld) of standardYn welk balanse de krediet giet.

In standert kredyt is 10 sats en in Pro kredyt 100 sats, plus alle bulk bonus; credits It giet om wat de faktuer dêrfoar ynsteld hat.

Antwurd

{
  "invoice_id": "b7d41e0c95a2f38e6c1d0e9a4b7f2c61d3e8a05f9b2c4d7e1a6f3b8c0d5e2a9f4",
  "payment_request": "lnbc100u1p5...",
  "amount_sats": 10000,
  "credits": 115,
  "tier": "pro",
  "expires_at": "2026-09-30T09:27:00Z",
  "status": "pending"
}
Status fanAs
400It earste krantsje kaam út yn april (unsupported_methodIt earste krantsje kaam út yn april (unsupported_currencyOer klasseboek en aktiviteiteplan, observaasje en registraasje yn 'e basisskoalle by Theun Meestringa(amount_too_smallOer klasseboek en aktiviteiteplan, observaasje yn 'e basisskoalle by Theun Meestringa(amount_too_largeIt earste krantsje kaam út yn april Nadere informatie (amount_out_of_range).
429Oer klasseboek en aktiviteiteplan, observaasje en registraasje yn 'e basisskoalle by Theun Meestringa(rate_limit_exceededYn Retry-After).
502It earste krantsje kaam út yn april (invoice_unavailableYn 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);

Kontrolearje in top-up

Befoarderet of de faktuer is betelle en, as it hat, fertsjintwurdiget de kredyt. Checking is wat kredyt it, dus nei betelling, kontrolearje oant de status is creditedKontrolearje werom dan is sûnder: de krediet landet ien kear, hoe oft jo fragen.

GET https://nymbot.ai/api/v1/topup/status/{invoice_id} - need in sleutel fan de nym dy't makke de faktuer.

status Yn pending (Wy binne noch net te beteljen) paid (Platte, mar noch net kreditearre; kontrolearje opnieuw), credited (Of in oare namme) of expired De plysje hat de plannen om de plannen te hâlden.

Antwurd

{
  "invoice_id": "b7d41e0c95a2f38e6c1d0e9a4b7f2c61d3e8a05f9b2c4d7e1a6f3b8c0d5e2a9f4",
  "status": "credited",
  "amount_sats": 10000,
  "credits": 115,
  "tier": "pro",
  "expires_at": null,
  "balance_credits": 523.33,
  "balance_sats": 52333
}
Status fanAs
400De id is net de 64-karaktere id fan 'e meitsje oprop.
404It earste krantsje kaam út yn april (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);

Wolle histoaryske

In rige per fraach: wat it wie, wat model, hoeveel tokens en wat it kostet. Gjin prompts of antwurden wurde bewarre, dus gjin wurde retournearre. Rijen wurde bewarre foar 90 dagen, nijste earst. In sleutel dy't har cap berikt, kin noch syn histoaryske lêzen.

GET https://nymbot.ai/api/v1/queries/history - nedich in API-sleutel, dy't syn eigen fragen sjen, of in Undertegrearre aanvraag Yn de nijs, dy't alle keuzes sjocht.

De fjildDe typeNeffensBeskriuwing
pageYnformaasje kin fûn wurde yn: QueryNo ynDefault 1, maksimaal 1000; in heger side is 400 invalid_value.
page_countYnformaasje kin fûn wurde yn: QueryNo ynRûnen per side. Default 20, op maksimum 100.
start_date
end_date
Ynstruksje foar QueryNo ynISO 8601 datums of oeren.
modelYnstruksje foar QueryNo ynAlleen dit model.
typeYnstruksje foar QueryNo ynchat, responses, messages, image, video, speech, transcription Of embedding.
all_keysBûtenlânske (query)No ynYn de sleutel: true Yn alle gefallen is der in nûmer.Default false.
key_idYnstruksje foar QueryNo ynYn de ûndersyk fan de fraach, of all_keys=trueAlleen dizze sleutel.

Antwurd

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

Skriuw dy yn foar Account Requests

It meitsjen, feroarjen en opheffen fan sleutels, de account resume en automatyske top-ups nimme gjin API-sleutel.

De app doet dit foar jo: alles yn syn De fire It earste krantsje kaam út yn april en it earste krantsje kaam út yn april en it earste krantsje kaam út yn april.

De handtekening is in Nostr-evenement fan soarte 27235 (NIP-98), ferstjoerd base64-kodearre yn de Authorization Header mei it woord Nostr Yn de foarkant:

De evenemint

{
  "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 de folsleine URL fan de fraach, fraach string ynklusyf, krekt as ferstjoerd.
  • method Dat is de HTTP metoade.
  • payload is de SHA-256 fan it rauwe fraachorganisme, yn hex. POST en PATCHYn it gefal fan it gefal is it gefal fan it gefal dat jo meitsje.
  • created_at It moat binne binnen 60 sekonden fan de klok fan de server.
  • Alle eveneminten wurkje ien kear, a GET Der binne in oantal oanmeldingen dy't opnommen wurde, dêr't in oantal oanmeldingen opnommen wurde. nonce tag mei in willekeurige wearde sa dy't twa fragen dy't yn itselde sekonde tekene binne noch ferskille.
  • It lichaam fan in tekene fraach kin maksimaal 64 KB wêze, en in lichaam moat Content-Type: application/json.

In mislearre gebeurtenis komt werom 401 missing_nostr_auth; ien dy't is slecht foarme, slecht tekene, te âld, of foar in ferskillende URL, metoade of body returns invalid_nostr_authYn de ûndersyk fan de ûndersyk komt de oanhâlding werom. nostr_auth_replayedDe handtekening wurdt kontrolearre foardat it korps is learde, en elk adres kin falen it 30 kear in minút (een IPv6 adres telt as syn helte /64); dêrnei krije it 429 Yn Retry-After.

Oer klasseboek en aktiviteiteplan, observaasje en registraasje yn 'e basisskoalle by Theun Meestringa(https://nymbot.ai, https://nymchat.app In side op in oare site krije gjin CORS-headers werom, dus it kin net lêzen wat se werombringe. OriginHy is no berikt.

De geheime sleutel

Undertekening nedich de geheime sleutel fan jo nim (de nsecAlleen sette it yn in skript op in masine dy't jo vertrouwt, lêze it út de omjouwing ynstee fan it skriuwen yn 'e file, en foarsteld de app as jo kinne.

Dizze helpers meitsje de header. De hjoeddeistige foarbylden op dizze side brûke se. Se lêze de geheime sleutel yn hex út NOSTR_SECRET_HEXDe cURL ien brûkt de Yn Kommandolinje-tool, dy't in nsec of hex sleutel hat, en sha256sum Yn de 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");
}

Yn de Account Summary

Wat de API-blêd fan 'e app oan 'e top beskriuwt: jo iepenbiere sleutel, beide balansjes, hoefolle sleutels binne aktive (nei herroppen of útgong), en de Automatyske top-up Ynstellings, of null De server is no oanbiedend.

GET https://nymbot.ai/api/v1/account Hy hat a Undertegrearre aanvraag.

Antwurd

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

It behear fan keys

De keywords fan 'e app binne allegear op 'e keywords. Undertegrearre aanvraagElke sleutel wurdt weromfûn yn dizze foarm, mei times yn ISO 8601 en bedragen yn sats:

Key objekten

{
  "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 It is genôch om in sleutel te ûntdekken, mar net om it te brûken.

Skriuw dy yn

GET https://nymbot.ai/api/v1/keys Hy tekene.

De fjildDe typeNeffensBeskriuwing
include_revokedBûtenlânske (query)No ynYnklusyf Revoked keys. Default false.

Antwurd

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

Skriuw dy yn foar Key

POST https://nymbot.ai/api/v1/keys Skriuw dy yn foar Returns 201.

De fjildDe typeNeffensBeskriuwing
nameStrjitteJa1 oant 40 karakters, ferskillende fan jo oare aktive sleutels (ignoring case).
limit_satsYntegraasjeNo ynDe kosten yn sats, op syn minst 1. Lea it út foar gjin cap.
reset_periodStrjitteNo yndaily, weekly Of monthlyDe behoeften limit_satsIt giet om in hûs dy't no wer ynsteld wurdt.
expire_atString of integerNo ynAs de sleutel stopt te wurkjen: in ISO 8601 tiid, of millisekonden sûnt 1970.

Yn Ljouwert (201)

{
  "data": {
    "id": "4f0c9a1be27d3856",
    "name": "laptop scripts",
    "hint": "sk-nymbot-Qm7x…c2Lw",
    "limit_sats": 20000,
    "reset_period": "monthly",
    "key": "sk-nymbot-Qm7x...c2Lw",
    "...": "the rest of the key object"
  }
}
Status fanAs
400It earste krantsje kaam út yn april Nadere informatie (duplicate_nameOer klasseboek en aktiviteiteplan, observaasje en registraasje yn 'e basisskoalle by Theun Meestringa(unknown_parameterIt earste krantsje kaam út yn april (too_many_keys).
429Oer klasseboek en aktiviteiteplan, observaasje en registraasje yn 'e basisskoalle by Theun Meestringa(rate_limit_exceededYn 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);

It lêzen fan in sleutel

GET https://nymbot.ai/api/v1/keys/{id} Hy tekene.

Kommentaar {"data": {…}} Yn it foarbyld, of 404 key_not_found As der no in identiteit is.

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

De skieding fan de sleutel

PATCH https://nymbot.ai/api/v1/keys/{id} Hy tekene.

Skriuw dy yn name, limit_sats, reset_period en expire_atDer binne net mear regels as by it meitsjen fan in sleutel. null Oer klasseboek en aktiviteiteplan, observaasje en registraasje yn 'e basisskoalle by Theun Meestringa(400 key_revokedYnterview {"data": {…}} Yn aktuele objekten.

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

Yn de sleutel

DELETE https://nymbot.ai/api/v1/keys/{id} Hy tekene.

It giet om in foarbyld, dat is op 'e list. revoked_at Yn it stuit, en kin fûn wurde mei include_revoked=trueDe lêste 50 keys binne bewarre; âldere keys wurde ferwiderd as in oare keys is ferwiderd.

Antwurd

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

Yn de auto-top-up

Connect in Lightning wallet mei Nostr Wallet Connect en Nymbot top op in balans op harsel as API-ûntgaven it laach rjochtsje. Undertegrearre aanvraag.

Hoe it wurket: nei in API-ferwachting wurdt laden nei it saldo dy't jo besykje te sjen, as dat saldo ûnder jo drempel is, meitsje Nymbot in faktuer foar jo top-up bedrach, freget jo wallet om it te beteljen, en fertsjintwurdiget de krediet. It heechst maksimaal ien kear elke 5 minuten foar elke nim en saldo, dus in gebrûk fan fragen kin de wallet net leegje. Utgaven yn 'e apps meitsje it net. De tiid en grootte fan 'e lêste top-up, en de lêste fout, binne yn 'e ynstellingen; as in betelling gie troch nei in fout, kontrolearje syn faktuer mei De top-up status Hy kreditearre it.

Foardat jo in wallet ferbine

Nymbot stjoert it encrypted en allinnich brûkt it om syn eigen top-up faktueren te beteljen, mar meitsje in ferbinding krekt foar dit, mei in útgongsbudget yn jo wallet, dus it meast dat it eartiids kin betelje is in nûmer keapje jo. pay_invoice.

Om in wallet te ferbinen

POST https://nymbot.ai/api/v1/nwc-auto-topup/connect Hy tekene.

De fjildDe typeNeffensBeskriuwing
nwc_urlStrjitteJaDe ferbining begjint nostr+walletconnect://Nymbot freegje de wallet foar get_info Yn it earst en yn it earst is it opnommen.
threshold_satsYntegraasjeJaIt bedriuw is op it stuit om de bedriuwsjouwing te fertsjinjen. minimaal 1000.
topup_satsYntegraasjeJaHoeveel om elke tiid oan te voegen. 1000 oant 1.000.000 satsen.
tierStrjitteNo ynpro (De foarbyld) of standardDe balans om te sjen en top up.

Antwurd

{
  "data": {
    "connected": true,
    "threshold_sats": 5000,
    "topup_sats": 20000,
    "tier": "pro",
    "last_topup_at": null,
    "last_topup_sats": null,
    "last_error": null
  }
}
Status fanAs
400It earste krantsje kaam út yn april (invalid_nwc_urlIt eardere eilân Wieringen is gjin part fan de gemeente.nwc_unreachableIt earste krantsje kaam út yn april Nadere informatie (nwc_rejectedIt earste krantsje kaam út yn april Nadere informatie (nwc_missing_permission(of in bedrach bûten de limieten.
501It earste krantsje kaam út yn april Nadere informatie (nwc_unavailableDat hâldt ek foar de oare twa eindpunten.

cURL

URL=https://nymbot.ai/api/v1/nwc-auto-topup/connect
BODY='{"nwc_url":"nostr+walletconnect://...","threshold_sats":5000,"topup_sats":20000,"tier":"pro"}'
curl "$URL" \
  -H "Authorization: $(nostr_auth POST "$URL" "$BODY")" \
  -H "Content-Type: application/json" \
  -d "$BODY"

Python

import json, os
import requests

url = "https://nymbot.ai/api/v1/nwc-auto-topup/connect"
body = json.dumps({
    "nwc_url": os.environ["NWC_URL"],
    "threshold_sats": 5000,
    "topup_sats": 20000,
    "tier": "pro",
}).encode()
res = requests.post(
    url,
    data=body,
    headers={"Authorization": nostr_auth("POST", url, body), "Content-Type": "application/json"},
)
print(res.json())

JavaScript

const url = "https://nymbot.ai/api/v1/nwc-auto-topup/connect";
const body = JSON.stringify({
  nwc_url: process.env.NWC_URL,
  threshold_sats: 5000,
  topup_sats: 20000,
  tier: "pro",
});
const res = await fetch(url, {
  method: "POST",
  headers: { "Authorization": nostrAuth("POST", url, body), "Content-Type": "application/json" },
  body,
});
console.log(await res.json());

It lêzen fan de settings

GET https://nymbot.ai/api/v1/nwc-auto-topup Hy tekene.

Retournearret itselde objekt as ferbining, mei connected: false Yn de oare gebieten null Yn it ferbûn is de ferbûn noch altyd ferbûn mei de ferbûn.

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

Disconnectsje

DELETE https://nymbot.ai/api/v1/nwc-auto-topup/connection Hy tekene.

It ferwideret de opgeslagen ferbiningstrjitte. No mear top-ups wurde makke. Om sûnder te wêzen, kinne jo ek de ferbining yn jo wallet revoke.

Antwurd

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

Betelje per fraach sûnder sleutel

De fixed-priis-endpoints kinne betelle wurde foar ien fraach op in tiid oer Lightning, mei gjin sleutel, gjin rekening en gjin saldo: POST /images/generations, POST /images/edits, POST /videos, POST /audio/speech, POST /audio/transcriptions, POST /audio/translations en POST /embeddingsChat, Antwurd en Messages moatte altyd in sleutel hawwe. In fraach dy't in sleutel draait, wurdt as normaal op it saldo ferbûn; de betellingstroom begjint allinnich doe't gjin sleutel ferstjoerd wurdt.

Nymbot sprekt twa ferzjes fan itselde idee, út ien backend: Lightning Labs' Yn 402 (Ook akseptearre ûnder syn âlde namme, LSATDe IETF-ûntwurking Betelling HTTP authentication skema mei de lightning De metoade en charge Doe't jo brûke wat jo kliïnten begripe.

Skriuw dy yn foar jo eigen server

Betelje sûnder in sleutel is op allinne as API_L402_SECRET hat op syn minst 32 random bytes, as hex (64 karakters) of base64 (44). openssl rand -hex 32In kortere of wedstrydige wearde ferstjoert de funksje en loget waarom. API_L402_SECRET_PREVIOUS foar in dei: credentials, status URL's en útdagingen makke ûnder it hâlden wurkje oant se verlopen.

De útdaging

Skriuw dy yn foar No Authorization as it geldig is, komt niks út, en jo krije 402 Payment Required mei in faktuer foar krekt wat dat fraach kostet: itselde priis dat in sleutel sou betelje, konvertearre by 10 sats in standert krediet of 100 sats in Pro krediet en rjochte oant in hiele sat (minimum 1 sat, en minimaal 0.05 krediet minimum). WWW-Authenticate De problemen foar itselde fakturaasje:

HTTP/1.1 402 Payment Required
Content-Type: application/problem+json; charset=utf-8
Cache-Control: no-store
WWW-Authenticate: L402 macaroon="AgJC...", invoice="lnbc2370n1..."
WWW-Authenticate: LSAT macaroon="AgJC...", invoice="lnbc2370n1..."
WWW-Authenticate: Payment id="kM9x...", realm="nymbot", method="lightning", intent="charge",
    request="eyJhbW91bnQiOiIyMzciLC...", description="Nymbot API POST /images/generations (237 sats)",
    digest="sha-256=:X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE=:", expires="2026-09-30T12:15:00.000Z",
    opaque="eyJlbmRwb2ludCI6IlBPU1QgL2ltYWdlcy9nZW5lcmF0aW9ucyJ9"

{
  "type": "https://paymentauth.org/problems/payment-required",
  "title": "Payment Required",
  "status": 402,
  "detail": "This request costs 237 sats. Pay the Lightning invoice, then send the identical request again ...",
  "challengeId": "kM9x...",
  "amount_sats": 237,
  "invoice": "lnbc2370n1...",
  "payment_hash": "9db1370f...",
  "expires_at": "2026-09-30T12:15:00.000Z",
  "error": { "message": "This request costs 237 sats. ...", "type": "payment_required", "code": "payment_required", "param": null }
}

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

It doel is om de finale, de Content-Type (sin media type en, foar multipart, syn grûn) en nei de SHA-256 fan de eksakte body bytes dy't jo stjoerde, en duorret 15 minuten. identiteit It earste krantsje kaam út yn april Nadere informatie Content-Type Oer klasseboek en aktiviteiteplan, observaasje en registraasje yn 'e basisskoalle by Theun Meestringa(/images/edits, /audio/transcriptions, /audio/translationsDe measte HTTP-biblioteken selektearje in nije grûn elke kear dat se in foarm kodearje, dus kodearje it ien kear en send dy bytes twa kear.

Elke adres kin freegje foar 30 útdagingen per minút (een IPv6 adres telt as syn helte /64). Requests whose address is not known share a stricter 10 per minute, en der is in totale cap op de útdagingen Nymbot problemen oer alle adressen; in fraach refused foardat in útdaging is makke (bygelyks mei in body dat is net geldig JSON) telt net foar it. Boppedat is it antwurd: 429 Yn Retry-AfterBetelle endpoints dy't belâne wurde sûnder in sleutel of credential berikken ek yn 'e algemiene limiet fan 120 unauthenticated requests per minute per address. Content-Type It is no application/json (Of multipart/form-data Yn de ûndersyk is it neamd. 415 En dêrfoar wurdt no noch in faktuer fûn.

Embeddings wurde priisje út in ramt fan de tokens yn 'e input, mei in 1.5× marge, sûnt de echte tellen wurdt allinich bekend. Skriuw dy yn foar.

Skriuw dy yn foar Betaling

Betelje de faktura mei elke Lightning wallet. De wallet jout jo de preimage, 64 hex karakters. Dan stjoere itselde fraach mei ien fan dizze:

SkemaHjoed
Yn 402Authorization: L402 <macaroon>:<preimage> (LSAT It wurket ek)
BetellingAuthorization: Payment <base64url JSON>Dêrom is de JSON {"challenge": {every parameter of the challenge, as sent}, "payload": {"preimage": "<hex>"}}

In betelle fraach antwurde krekt as ien makke mei in sleutel, bûten dat de nymbot It object hat gjin balanse fjilden: {"payment": "l402", "tier": "pro", "paid_sats": 237, "charged_sats": 237}En der is no X-Nymbot-Balance-Sats In fraach betelle mei de Payment Scheme krije ek in Payment-Receipt header (base64url JSON mei de challenge id, de betaling hash as reference, status en timestampPayed requests binne net boud oan gjin nimme, dus dy't se net foarkomme yn de query histoary.

Status fanAs
402 payment_already_usedElke betelling betelt foar ien fraach. De antwurd is in frisse útdaging foar dizze fraach, dus in kliïnt dy't syn lêste credential caches (as lnget It giet net om it lân opnommen.
402 payment_mismatchIt earste krantsje kaam út yn 'e finale. Content-Type Of betelje minder as dizze fraach no kostet. In nije útdaging foar dizze fraach komt mei it; as de betelling wie te klein, wat jo betelle komt werom as in Skriuw dy yn foar (refund_token en refund_sats Yn it lichem.
402 payment_expiredMear as 15 minuten folge sûnt de útdaging. In frisse útdaging komt mei it. As de preimage beskriuwt dat jo betelle hawwe, komt wat jo betelle hawwe werom as in Skriuw dy yn foar (refund_token en refund_sats Yn 'e hûnderttûzen dagen wurdt de credential dan útbrocht.
401 invalid_preimageDe foarbyld hat gjin hash oan de betelling hash fan de faktuer.
401 invalid_payment_credentialDe credential is misformed, waard feroarre nei Nymbot útstelde it, of nammen in betelling hash Nymbot hat noch gjin faktura útsteld foar.
429 rate_limit_exceededMear as 30 credentials of sleutels dy't mislearre te verifyjen kom fan dit adres yn in minút, of ien refund token waard ferstjoerd mear as 60 kear yn in minút. 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ïnten binne yn mppx mei in Lightning metoade behannele de Payment útdaging sels; rjochtsje harren oan it eindpunt en laat se betelje.

De videos

De betelling POST /videos Antwurd 202 It is as in sleutel ien, plus a status_url: GET It wurdt tekene en wurket foar 24 oeren, as de baan is hâlden.

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

Hâld it refund_token Oer klasseboek en aktiviteiteplan, observaasje en registraasje yn 'e basisskoalle by Theun Meestringa(GET /api/v1/l402/refunds Antwurd "status": "pending"Yn it gefal fan de betelling is de status URL tafoege. refund_sats Foar in refunded job, mar nooit de token, dus de dealing fan de status-URL deelt de refondering net.

Refinânsje

As in betelle oanfolling mislitte en de provider Nymbot ferbûn foar de oertsjûging, wurdt de betelling hâlden en de fout sêft dat, mei charged_satsYn it gefal fan 'e ûndersyk, as it feroarsake is, is it feroarsjend. Skriuw dy yn foar It waard wat jo betelle:

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

Unbenutste dielen komme werom op de selde manier: as jo frege foar twa foto's en ien mislearre unfolled, de sukses reaksje's nymbot Object draait in refund token foar de mislearre ien; in transkripsje dy't lengte kin net leare up front wurdt prize foar de langste de lêzer kin wêze (nie mear as 30 minuten), en it ferskil nei de echte lengte komt werom as in refund token; as it útkomt te langer as 30 minuten, it wurdt weigere mei 413 En alle betelling komt werom. Embeddings return wat de estimate hâldt werom. A failed video refunds to the token its submission returned.

In refund token is in willekeurige 256-bits koade. Nymbot stjoert allinnich syn hash, en it ferliedt nei 30 dagen. It hat in sat saldo, en jo kinne:

  • Betelje mei it. Skriuw dy Authorization: Bearer REFUND-… Oer klasseboek en aktiviteiteplan, observaasje en registraasje yn 'e basisskoalle by Theun Meestringa(refund_token_sats Yn de nymbot In token waard minder waard as de fraach antwurde 402 refund_insufficientYn de ûndersyk fan de ûndersyk is de rjochtsje op 'e rjochtsje neamd.
  • Check it yn. GET /api/v1/l402/refunds De header komt werom. {"sats": 237, "status": "open", "expires_at": "..."}.
  • In token kin maksimaal 60 kear yn 'e minút brûkt wurde.
  • Befoarderje it yn in nym. Skriuw it yn Skriuw dy yn foar Gift yn de Nymbot app, of belje POST /api/v1/l402/refunds/redeem Yn A Undertegrearre aanvraag en {"refund_token": "REFUND-...", "balance": "standard"} (Of "pro"De hiele credits gie nei it saldo (10 sats elk op standert, 100 op Pro); sats dy't net meitsje in hiele krediet bliuwe op it token foar API fragen.

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"

Antwurd

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