Link

For developers

Create and send ordinals

Your program asks Link to inscribe a picture, mint a token, or send one it already holds. Link holds the keys. You never open the wallet.

Start

The list of these pages is Nexus Developers. Connect first. That page is Connect an app to Link. This page is only the ordinal door. BSV payments and your own note stay on that other page.

Link listens on http://127.0.0.1:21736. The process that opens the socket must be the exe you signed. A script hosted by python.exe or node.exe is that host. Ship an exe.

The coin you already have

A code-signing-ID with spend is the permission. There is no extra ordinal scope. Do not ask for a new coin just to inscribe.

Link always asks

Auto and the Security lamp do not cover a picture, a token, or a send. The person presses Yes in Link. Wait up to 3 minutes.

Rules

Copy these. Do not invent a second way.

One picture

One file becomes one 1-sat inscription. Do not inscribe a pile of pictures to stand in for a supply.

One token

One deploy+mint. supply is the whole amount. An optional picture is the icon, not the supply.

Size

PNG, JPEG, WebP, or GIF. Hard stop 100 KB. Above 40 KB, Link tells the person it is a lot for an icon.

Who pays the fee

The miner fee comes from the messenger account. The ordinals account is never the fee. You do not choose the receive address. The new ordinal lands there.

Where a send goes

to is an @nexusnode.link name, or a 1… address. Link uses that name’s ordinals address. Any other paymail is refused.

Leave these alone

Nexus ID coins and Art Ownership pieces are not in the list. This door will not send them. Do not read the wallet folder.

List

GET http://127.0.0.1:21736/v1/app/ordinals. No body. Link does not ask. Call this before a send, and again after a mint if you attached a picture.

{
  "ok": true,
  "ordinals": [
    {
      "origin": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef_0",
      "kind": "picture",
      "type": "image/png",
      "name": "badge"
    },
    {
      "origin": "fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210_0",
      "kind": "token",
      "type": "application/bsv-20",
      "name": "Peyton",
      "amount": "1000",
      "token_id": "fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210_0",
      "sym": "PEY",
      "icon": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef_0"
    }
  ]
}

origin is the id you send later. amount is the units on that coin. token_id is the deploy. icon is the picture origin, when the mint had one. A picture row has no amount.

Picture

POST http://127.0.0.1:21736/v1/app/ordinal/picture with Content-Type: application/json. data is standard base64 of the file bytes. Not a path. Not a URL.

{
  "type": "image/png",
  "name": "badge",
  "data": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg=="
}

type is image/png, image/jpeg, image/webp, or image/gif. name is optional, up to 48 characters, no |. Link asks, then inscribes.

Token

POST http://127.0.0.1:21736/v1/app/ordinal/token. sym is 1 to 16 letters or numbers. supply is a string of digits, no leading zero. decimals is a JSON number from 0 to 18. Leave it out for 0.

{
  "sym": "PEY",
  "supply": "1000",
  "decimals": 0,
  "name": "Peyton"
}

Add type and data only when the token has one icon picture. Same picture rules as above. Omit both when there is no picture. One without the other is bad request.

{
  "sym": "PEY",
  "supply": "1000",
  "name": "Peyton",
  "type": "image/png",
  "data": "base64-of-the-png"
}

The txid in the answer is the token. The picture, when you sent one, is icon on the list. supply is text. Do not send it as a number.

Send

POST http://127.0.0.1:21736/v1/app/ordinal/send. origin is the origin from the list. A 64-character id means vout 0.

{
  "origin": "fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210_0",
  "to": "someone@nexusnode.link",
  "amount": "10"
}

A picture

Leave amount out. The whole inscription moves. A number on a picture is refused.

A token

amount is a digit string, the units to send. Leave it out, or send "all", for every unit on that coin.

The txid in the answer is the transfer. List again if you need the coin that remains.

The answer

Read ok first. A yes from a picture or a token looks like this. origin is that new inscription.

{
  "ok": true,
  "txid": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
  "origin": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef_0",
  "paymail": "payer@nexusnode.link"
}

A send has txid and paymail. It does not add origin. A no looks like {"ok":false,"error":"cancelled"}.

C#

The signed exe is this program. Timeout is 3 minutes because Link waits for Yes. Delete a call you do not need.

using System.Net.Http;
using System.Text;

const string Link = "http://127.0.0.1:21736";
using var http = new HttpClient();
http.Timeout = TimeSpan.FromMinutes(3);

string list = await http.GetStringAsync(Link + "/v1/app/ordinals");

string picture = await (await http.PostAsync(Link + "/v1/app/ordinal/picture",
    new StringContent("""
    {"type":"image/png","name":"badge","data":"PASTE_BASE64"}
    """, Encoding.UTF8, "application/json"))).Content.ReadAsStringAsync();

string token = await (await http.PostAsync(Link + "/v1/app/ordinal/token",
    new StringContent("""
    {"sym":"PEY","supply":"1000","name":"Peyton"}
    """, Encoding.UTF8, "application/json"))).Content.ReadAsStringAsync();

string send = await (await http.PostAsync(Link + "/v1/app/ordinal/send",
    new StringContent("""
    {"origin":"PASTE_ORIGIN","to":"someone@nexusnode.link","amount":"10"}
    """, Encoding.UTF8, "application/json"))).Content.ReadAsStringAsync();

Python

Run this from the signed exe, not from python.exe, or Link answers not allowed.

import json, urllib.request
LINK = "http://127.0.0.1:21736"

def call(method, path, body=None):
    data = None if body is None else json.dumps(body).encode()
    req = urllib.request.Request(LINK + path, data=data, method=method)
    if data:
        req.add_header("Content-Type", "application/json")
    with urllib.request.urlopen(req, timeout=180) as res:
        return json.load(res)

print(call("GET", "/v1/app/ordinals"))
print(call("POST", "/v1/app/ordinal/picture",
    {"type": "image/png", "name": "badge", "data": "PASTE_BASE64"}))
print(call("POST", "/v1/app/ordinal/token",
    {"sym": "PEY", "supply": "1000", "name": "Peyton"}))
print(call("POST", "/v1/app/ordinal/send",
    {"origin": "PASTE_ORIGIN", "to": "someone@nexusnode.link", "amount": "10"}))

JavaScript

Same rule. The signed exe opens the socket. node.exe is not that exe.

const link = "http://127.0.0.1:21736";
async function call(method, path, body) {
  const res = await fetch(link + path, {
    method,
    headers: body ? { "Content-Type": "application/json" } : undefined,
    body: body ? JSON.stringify(body) : undefined,
    signal: AbortSignal.timeout(180000)
  });
  return res.json();
}
console.log(await call("GET", "/v1/app/ordinals"));
console.log(await call("POST", "/v1/app/ordinal/token",
  { sym: "PEY", supply: "1000", name: "Peyton" }));

When it fails

not found

That Link is older than this door. They need a Link that has this page’s calls.

not allowed

The socket is not the signed exe, the coin has no spend, or no paymail is published.

bad request

A field is missing, the wrong type, or the picture is not PNG, JPEG, WebP, or GIF. supply and amount are strings of digits.

cancelled

They pressed No. Do not retry in a loop.

not enough funds

The messenger account cannot cover the miner fee. Do not take the fee from an ordinal.

not in this wallet

The error says the ordinal is not in this wallet. List again and use that origin.

A file over 100 KB says file is over 100 KB. Shrink it first. A picture send with a number says the whole inscription is what moves. Another paymail says to use a 1… address or an @nexusnode.link name.