Skip to main content

Creating Marks

Learn how to create marks to timestamp and attribute content on the Bitmark blockchain.

What is a Mark?

A mark is a special transaction that:

  • Records a reference (URL, address, content hash) on the blockchain
  • Transfers value to content creators
  • Builds your reputation as a curator
  • Creates permanent, verifiable proof of endorsement

Mark Types

TypeCodeUse Case
URL0x01Web content, articles
Address0x02Creator tips, attribution
Content0x03File hashes, IPFS CIDs
Nostr Profile0x04Mark a person on Nostr
Git Commit0x05Code contributions
Document0x06Proof of existence
Timestamp0x07Cross-chain timestamping
Nostr Event0x08Mark specific notes
URI0x09Linked data identifiers

Using bitmark-api

The easiest way to create marks is via the REST API.

Prerequisites

# Install and run bitmark-api
git clone https://github.com/project-bitmark/bitmark-api.git
cd bitmark-api
npm install
npm start

Create a URL Mark

curl -X POST http://localhost:3000/api/mark \
-H "Content-Type: application/json" \
-d '{
"type": 1,
"reference": "https://example.com/great-article",
"privateKey": "your-private-key-wif"
}'

Response:

{
"txid": "abc123...",
"referenceHash": "8823a419...",
"fee": 1000000
}

Create an Address Mark

curl -X POST http://localhost:3000/api/mark \
-H "Content-Type: application/json" \
-d '{
"type": 2,
"reference": "bCreatorAddress...",
"amount": 10000000,
"privateKey": "your-private-key-wif"
}'

Create a Document Mark

# First compute the file hash
FILE_HASH=$(sha256sum document.pdf | cut -d' ' -f1)

curl -X POST http://localhost:3000/api/mark \
-H "Content-Type: application/json" \
-d "{
\"type\": 6,
\"referenceHash\": \"$FILE_HASH\",
\"privateKey\": \"your-private-key-wif\"
}"

Using JavaScript

const crypto = require('crypto');
const BitmarkAPI = require('bitmark-api-client');

const client = new BitmarkAPI({
baseUrl: 'http://localhost:3000'
});

// Mark a URL
async function markURL(url, privateKey) {
const referenceHash = crypto
.createHash('sha256')
.update(url)
.digest('hex');

const mark = await client.createMark({
type: 1, // URL
reference: url,
referenceHash: referenceHash,
privateKey: privateKey
});

console.log(`Mark created: ${mark.txid}`);
return mark;
}

// Mark a Nostr profile
async function markNostrProfile(pubkey, privateKey) {
// Nostr pubkeys are already 32 bytes, use directly
const mark = await client.createMark({
type: 4, // Nostr Profile
referenceHash: pubkey, // 64 hex chars
privateKey: privateKey
});

return mark;
}

// Mark a document
async function markDocument(filePath, privateKey) {
const fs = require('fs');
const fileBuffer = fs.readFileSync(filePath);
const referenceHash = crypto
.createHash('sha256')
.update(fileBuffer)
.digest('hex');

const mark = await client.createMark({
type: 6, // Document
referenceHash: referenceHash,
privateKey: privateKey
});

console.log(`Document marked: ${mark.txid}`);
console.log(`Reference hash: ${referenceHash}`);
return mark;
}

Manual Mark Creation

For advanced users who want to create marks directly.

1. Build Mark Data

function buildMarkData(type, referenceHash) {
// Magic: "MRK" (0x4d524b)
// Version: 0x01
// Type: 0x01-0x09
// Reference Hash: 32 bytes

const data = Buffer.alloc(37);
data.write('MRK', 0);
data.writeUInt8(0x01, 3); // Version
data.writeUInt8(type, 4); // Type
Buffer.from(referenceHash, 'hex').copy(data, 5);

return data;
}

2. Create OP_RETURN Output

function createOpReturnScript(markData) {
// OP_RETURN (0x6a) + PUSH 37 (0x25) + data
const script = Buffer.alloc(39);
script.writeUInt8(0x6a, 0); // OP_RETURN
script.writeUInt8(0x25, 1); // Push 37 bytes
markData.copy(script, 2);
return script;
}

3. Build Transaction

const bitcoin = require('bitcoinjs-lib');

function createMarkTransaction(utxo, markData, changeAddress, privateKey) {
const network = bitcoin.networks.bitcoin; // Adjust for Bitmark

const tx = new bitcoin.TransactionBuilder(network);

// Add input
tx.addInput(utxo.txid, utxo.vout);

// Add OP_RETURN output
const opReturn = createOpReturnScript(markData);
tx.addOutput(opReturn, 0);

// Add change output
const fee = 10000; // 0.0001 BTM
const change = utxo.value - fee;
tx.addOutput(changeAddress, change);

// Sign
const keyPair = bitcoin.ECPair.fromWIF(privateKey, network);
tx.sign(0, keyPair);

return tx.build().toHex();
}

4. Broadcast

bitmark-cli sendrawtransaction <hex>

Or via API:

curl -X POST http://localhost:3000/api/tx/broadcast \
-H "Content-Type: application/json" \
-d '{"hex": "<transaction-hex>"}'

Storing References

Marks only store hashes on-chain. Store the original reference off-chain:

# Store reference for later lookup
curl -X POST http://localhost:3000/api/reference \
-H "Content-Type: application/json" \
-d '{
"reference": "https://example.com/article",
"hash": "8823a419..."
}'

# Retrieve reference by hash
curl http://localhost:3000/api/reference/8823a419...

Verifying Marks

Find Marks by Hash

curl http://localhost:3000/api/marks?hash=8823a419...

Verify Mark Exists

async function verifyMark(reference) {
const hash = crypto
.createHash('sha256')
.update(reference)
.digest('hex');

const marks = await client.getMarks({ hash });

if (marks.length > 0) {
console.log(`Reference marked ${marks.length} times`);
console.log(`First marked at block ${marks[0].blockHeight}`);
return true;
}

console.log('Reference not marked');
return false;
}

Best Practices

Choosing Mark Amounts

Content ValueSuggested Mark
Amusing comment0.001 BTM
Helpful article0.01 BTM
Valuable resource0.1 BTM
Life-changing1+ BTM

Before Marking

  1. Verify content exists and is what you expect
  2. Check if already marked (avoid duplicates)
  3. Have sufficient balance for fee

After Marking

  1. Store the reference off-chain
  2. Note the transaction ID for your records
  3. Share the mark (builds both reputations)

Common Issues

"Insufficient funds"

Need at least mark amount + transaction fee (~0.001 BTM)

"Reference too long"

Only the hash is stored on-chain. Long references are automatically hashed.

"Invalid reference hash"

Ensure hash is 64 hex characters (32 bytes)

See Also