REST API.

For teams that want their own interface rather than the embed widget. The API prepares transactions; the user's wallet still signs and submits them, so nothing here can move funds.

Updated 2026-07-29 · HoodLock Team

Base URL and format#

All endpoints live under https://hoodlock.tech/api/dev, take and return JSON, and send permissive CORS headers so they work from a browser as well as a server.

Note

These routes carry X-Robots-Tag: noindex, which is why this page, not the JSON — is the documented surface.

Authentication#

One credential: a public API key of the form pk_ followed by 48 hex characters. You get it by registering a developer handle in the developer dashboard.

MethodHow the key is passed
GETquery string — ?key=pk_…
POSTa key field in the JSON body
Careful

There is no header form. The CORS policy allows Content-Type only, so an Authorization header will be blocked on cross-origin requests. Use the query string or the body field.

Note

The key is public on purpose, in the same sense as a Stripe publishable key. It credits locks to you and does nothing else. It cannot move funds, read balances or reach admin. Claiming earnings requires a signature from your wallet, which the key cannot produce. Embedding it in frontend source is the intended use.

GET /api/dev/config#

Everything your interface needs to build a transaction. Call it once at boot rather than hardcoding addresses or fees — the fee is read from the contracts and can change.

shell
curl "https://hoodlock.tech/api/dev/config?key=pk_YOUR_KEY"
json
{
  "chainId": 4663,
  "rpc": "https://rpc.mainnet.chain.robinhood.com",
  "explorer": "https://robinhoodchain.blockscout.com",
  "locker": "0xd0f7d8c6e9f6d80c297bebe4f7fd1b9c8125c32f",
  "burner": "0x6bf43ca706faa8ea46803299c191484e82280652",
  "vesting": "0x910e19bcC4bce46999994Ed7297E0Fc4431ec72E",
  "fees":    { "lock": "5000000000000000", "burn": "5000000000000000", "vesting": "5000000000000000" },
  "feesEth": { "lock": 0.005, "burn": 0.005, "vesting": 0.005 },
  "commission": 0.5,
  "code": "your-handle"
}

feeWei and feeEth are also present at the top level and hold the lock fee; they predate the per-product fields and are kept so older integrations keep working. Prefer fees. A product that is not configured on the chain returns null.

Errors: 404 {"error":"unknown key"}, or 503 if the database is unavailable.

Transaction intents#

Each intent validates your inputs, encodes the call and returns an unsigned transaction. You submit it from the user's wallet.

json
{ "to": "0x…", "data": "0x…", "value": "5000000000000000", "chainId": 4663, "note": "…" }
Careful

For locks and vesting the token must be approved for to first — the intent covers the HoodLock call, not the ERC-20 approval. The note field in the response says so too.

POST /api/dev/lock-intent#

FieldTypeNotes
keystringYour API key.
tokenaddressThe ERC-20 to lock.
amountstringInteger string, in the token's smallest unit.
unlockTimestringUnix seconds. Must be in the future.
shell
curl -X POST https://hoodlock.tech/api/dev/lock-intent \
  -H "content-type: application/json" \
  -d '{"key":"pk_YOUR_KEY",
       "token":"0x0Bd7D308f8E1639FAb988df18A8011f41EAcAD73",
       "amount":"1000000000000000000000",
       "unlockTime":"1790000000"}'

POST /api/dev/burn-intent#

Fields: key, token, amount. Returns 404 if burning is not configured on the chain.

Irreversible

Burning is irreversible. Show the user what is about to happen in your own UI — the intent endpoint will not stop them.

POST /api/dev/vesting-intent#

FieldTypeNotes
keystringYour API key.
tokenaddressThe ERC-20 to vest.
amountstringInteger string, smallest unit.
beneficiaryaddressWho receives the tokens.
endstringUnix seconds. Must be after start.
startstring, optionalDefaults to now.
cliffstring, optionalDefaults to start. Must satisfy start ≤ cliff ≤ end.

The contract's rules are checked here, so a schedule that would revert generally never reaches a signature prompt.

Careful

One case slips through. This endpoint checks end - start ≥ 86400, while the contract checks end > block.timestamp + MIN_DURATION. A back-dated start can satisfy the API and still revert on-chain. Validate against block.timestamp in your own UI.

POST /api/dev/attribute#

Credits a wallet to your handle so locks it makes afterwards earn you commission. Call it when the user connects, before they transact.

shell
curl -X POST https://hoodlock.tech/api/dev/attribute \
  -H "content-type: application/json" \
  -d '{"key":"pk_YOUR_KEY","wallet":"0xUSER"}'

Returns {"ok":true}, or {"ok":false,"reason":"…"} with HTTP 200 — a refusal is an expected outcome, not an error.

ReasonMeaning
bad-walletNot a valid address.
already-attributedAnother partner got there first. First touch wins and is permanent.
established-walletThe wallet was already known to HoodLock before you introduced it.
already-lockedThe wallet has locked, burned or vested before.
unknown-codeThe handle behind the key no longer exists.
Note

Only genuinely new wallets count, and only actions taken after attribution. That is deliberate: it stops a partner from claiming users who were already here. It also means calling this endpoint on every page load is harmless — repeats are refused, not double-counted.

Rate limits#

EndpointRequests per minute per IP
/api/dev/attribute30
*-intent60

Over the limit returns 429 {"error":"rate limited"}.

A minimal integration#

javascript
const cfg = await (await fetch(`/api/dev/config?key=${KEY}`)).json();

// after the user connects
await fetch("https://hoodlock.tech/api/dev/attribute", {
  method: "POST", headers: { "content-type": "application/json" },
  body: JSON.stringify({ key: KEY, wallet: account }),
});

// 1. approve, 2. lock
const intent = await (await fetch("https://hoodlock.tech/api/dev/lock-intent", {
  method: "POST", headers: { "content-type": "application/json" },
  body: JSON.stringify({ key: KEY, token, amount, unlockTime }),
})).json();

await walletClient.sendTransaction({
  to: intent.to, data: intent.data, value: BigInt(intent.value),
});

Getting paid#

Partners earn 50% of the fee on every action generated by a wallet they introduced. Earnings accrue automatically and are claimed from the developer dashboard with a wallet signature, once the balance passes $10. The API key plays no part in that. See the embed widget page.