justi.fun
§08 · API

Webhooks

Signed deliveries for launches, trades, graduations, burns, buybacks, payouts and claims.

Webhooks need an API key (a free one works). Register with POST /v1/webhooks; the response has the signing secret, shown once. A free key can hold 5 webhooks, a partner key 50.

curl
curl -X POST "https://api.justi.fun/v1/webhooks" \
  -H "Authorization: Bearer $JUSTI_API_KEY" -H "Content-Type: application/json" \
  -d '{"url":"https://example.com/hooks/justi","events":["launch.created","trade","coin.graduated"],"mints":null}'

Events

typeWhenAlso on WebSocket
launch.createdA coin went live (any launch, site or API). Carries partner when built with a key.launches
tradeA trade on the curve or on DAMM v2, within 1 hour of its block time. Limit to coins with mints.trades, trades:<mint>
coin.graduatedThe curve filled and the DAMM v2 pool was found.graduations
burnA buyback was burned ($JUSTI or the coin itself after the sniper window).burns
buybackA buyback swap landed.
payoutA holder was paid SOL.
creator.claimA creator claimed fees through the site or API.
webhook.testSent by POST /v1/webhooks/{id}/test.

Verify signatures

Each delivery is POST with JSON { id, type, createdAt, data } and headers Justi-Event, Justi-Delivery and Justi-Signature: t=<unix>,v1=<hex>, where v1 = HMAC-SHA256(secret, "<t>.<raw body>"). Check it against the raw bytes you received, before parsing, and refuse timestamps more than 5 minutes off.

import { verifyWebhook } from "@justifun/sdk";

// Express: keep the raw body for this route
app.post("/hooks/justi", express.raw({ type: "application/json" }), async (req, res) => {
  const ok = await verifyWebhook(req.body, req.get("Justi-Signature"), process.env.JUSTI_WEBHOOK_SECRET!);
  if (!ok) return res.status(400).end();
  const event = JSON.parse(req.body.toString("utf8")); // { id, type, createdAt, data }
  // dedupe on event.id, then handle event.type
  res.status(200).end();
});

Retries and disabling

  • Any 2xx is success. Redirects are not followed. Answer within a few seconds and do slow work after.
  • Up to 8 attempts, waiting 30 s x 3^(n-1) capped at 6 hours, with 20% jitter: about 9 hours in all.
  • After 5 failures in a row a webhook gets one probe delivery per second until it answers; the rest wait.
  • After 25+ failed attempts in a row over at least an hour it is disabled and its queue is cancelled. PATCH { enabled: true } turns it back on.
  • Order is not guaranteed. Dedupe on id. History is never replayed into a new webhook.
  • The delivery log (GET /v1/webhooks/{id}/deliveries) shows status, attempts, last status code, error, duration and payload.

Payloads

Captured from real deliveries:

trade
{"id":"evt_24","type":"trade","createdAt":1793110075,"data":{"mint":"AxqRtyYz9pNyqZZfvFwjvhJbnQfNaHMofZH43Mhb23Da","ticker":"","venue":"curve","side":"buy","wallet":"Hkm9m53BVZsV3SRf23zzq6BVEpcsfz2M6Ucsa837Nzk7","solLamports":"8212500000","tokenAmount":"22249603316556","priceSol":4.463611817123742e-7,"marketCapSol":446.3611817123742,"signature":"5bqKP1FxfcYeQgRf5VH6dQ5QaSdvvsJMy5sJ1Uew5QY1d8twY5ZTzjpvY9bUPBbyf6eRY4LMLzFX6ZXJn91WSn7g","ix":"4","slot":462098540,"ts":1793110057,"inWindow":true}}
coin.graduated
{"id":"evt_23","type":"coin.graduated","createdAt":1793110030,"data":{"mint":"8Qs4Jb16QKuZyb13dKAbjw3DpbDM7KqUmqeajjaa3bMk","ticker":"","name":"","curvePool":"DvyctJAp1oJ4gsQMgZ9tMbZzYrPUGf8dTk5rk3RyqwrH","dammPool":"7JSjaVcgQasuSRbjs1rY7T4zBxnnxXWQ4BmKETn7wuqV","migrationSignature":"3ZQ72yceM8kNNLWvwk9J6ZZ3yGt1ooXj9PS8qdW8m5A3Djw3nwS2nruDPnmKSDbwkCAG8H1WtXHiVVzNV9opx3sc","ts":1793057896}}
launch.created (fields)
{"id":"evt_...","type":"launch.created","createdAt":0,"data":{"mint":"...JUST","name":"...","ticker":"...","description":"...","image":"https://api.justi.fun/v1/image/<launchId>","metadataUri":"https://api.justi.fun/v1/meta/<launchId>","creator":"<wallet>","curvePool":"...","config":"...","tier":"split-50","activationTs":0,"signature":"<create tx>","devBuyTokens":"3680660059553","partner":null}}
Note
The other payloads: buyback {id, mint, kind, lamports, tokensBought, route, swapSignature, ts}, burn {id, mint, kind, lamports, tokensBurned, swapSignature, burnSignature, ts}, payout {id, wallet, lamports, signature, ts}, creator.claim {mint, wallet, lamports, signature, ts}. The SDK exports types for all of them.

Reference

#Register a webhook (the secret is shown once)

POST/v1/webhooks
Key requiredRate class manageBody 8kbcreateWebhook

Deliveries are POSTs of {id,type,createdAt,data} with headers Justi-Event, Justi-Delivery and Justi-Signature: t=<unix>,v1=<hex HMAC-SHA256(secret, "<t>.<raw body>")>. Any 2xx is success. Retries: 8 attempts, 30s x 3^(n-1) (cap 6h, +-20% jitter). 25 failures in a row disable the webhook. trade events can be limited to mints.

Body

FieldTypeAbout
urlrequiredstringhttps only, public hosts only. max 500 chars
eventsrequired"launch.created" | "trade" | "coin.graduated" | "burn" | "buyback" | "payout" | "creator.claim"[]
mintsaddress[] | nullOnly for trade events. Null or absent: every coin.
descriptionstring | nullmax 200 chars
Response WebhookCreated
FieldTypeAbout
webhookrequiredWebhook
idrequiredstringUUID.
urlrequiredstring
eventsrequired"launch.created" | "trade" | "coin.graduated" | "burn" | "buyback" | "payout" | "creator.claim"[]
mintsrequiredaddress[] | null
descriptionrequiredstring | null
enabledrequiredboolean
disabledReasonrequiredstring | null
consecutiveFailuresrequiredinteger
createdAtrequiredintegerUnix time in seconds.
updatedAtrequiredintegerUnix time in seconds.
lastSuccessAtrequiredinteger | nullUnix time in seconds.
lastFailureAtrequiredinteger | nullUnix time in seconds.
secretrequiredstringHMAC secret. Store it now; it is not shown again.
Errors400 bad_request, invalid_params, invalid_json401 unauthorized409 conflict413 payload_too_large429 rate_limited500 internal
curl -X POST "https://api.justi.fun/v1/webhooks" \
  -H "Authorization: Bearer $JUSTI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://example.com/hooks/justi","events":["launch.created","trade"],"description":"hold the line."}'

#Webhooks of this API key

GET/v1/webhooks
Key requiredRate class managelistWebhooks
Response object
FieldTypeAbout
webhooksrequiredWebhook[]
idrequiredstringUUID.
urlrequiredstring
eventsrequired"launch.created" | "trade" | "coin.graduated" | "burn" | "buyback" | "payout" | "creator.claim"[]
mintsrequiredaddress[] | null
descriptionrequiredstring | null
enabledrequiredboolean
disabledReasonrequiredstring | null
consecutiveFailuresrequiredinteger
createdAtrequiredintegerUnix time in seconds.
updatedAtrequiredintegerUnix time in seconds.
lastSuccessAtrequiredinteger | nullUnix time in seconds.
lastFailureAtrequiredinteger | nullUnix time in seconds.
Errors400 invalid_params401 unauthorized429 rate_limited500 internal
curl "https://api.justi.fun/v1/webhooks" \
  -H "Authorization: Bearer $JUSTI_API_KEY"

#One webhook

GET/v1/webhooks/{id}
Key requiredRate class managegetWebhook

Parameters

NameTypeAbout
idrequiredpathstringUUID.
Response Webhook
FieldTypeAbout
idrequiredstringUUID.
urlrequiredstring
eventsrequired"launch.created" | "trade" | "coin.graduated" | "burn" | "buyback" | "payout" | "creator.claim"[]
mintsrequiredaddress[] | null
descriptionrequiredstring | null
enabledrequiredboolean
disabledReasonrequiredstring | null
consecutiveFailuresrequiredinteger
createdAtrequiredintegerUnix time in seconds.
updatedAtrequiredintegerUnix time in seconds.
lastSuccessAtrequiredinteger | nullUnix time in seconds.
lastFailureAtrequiredinteger | nullUnix time in seconds.
Errors400 invalid_params401 unauthorized404 not_found429 rate_limited500 internal
curl "https://api.justi.fun/v1/webhooks/<id>" \
  -H "Authorization: Bearer $JUSTI_API_KEY"

#Change a webhook; enabled=true re-enables one that was disabled

PATCH/v1/webhooks/{id}
Key requiredRate class manageBody 8kbupdateWebhook

Parameters

NameTypeAbout
idrequiredpathstringUUID.

Body

FieldTypeAbout
urlstringmax 500 chars
events"launch.created" | "trade" | "coin.graduated" | "burn" | "buyback" | "payout" | "creator.claim"[]
mintsaddress[] | null
descriptionstring | nullmax 200 chars
enabledboolean
Response Webhook
FieldTypeAbout
idrequiredstringUUID.
urlrequiredstring
eventsrequired"launch.created" | "trade" | "coin.graduated" | "burn" | "buyback" | "payout" | "creator.claim"[]
mintsrequiredaddress[] | null
descriptionrequiredstring | null
enabledrequiredboolean
disabledReasonrequiredstring | null
consecutiveFailuresrequiredinteger
createdAtrequiredintegerUnix time in seconds.
updatedAtrequiredintegerUnix time in seconds.
lastSuccessAtrequiredinteger | nullUnix time in seconds.
lastFailureAtrequiredinteger | nullUnix time in seconds.
Errors400 bad_request, invalid_params, invalid_json401 unauthorized404 not_found413 payload_too_large429 rate_limited500 internal
curl -X PATCH "https://api.justi.fun/v1/webhooks/<id>" \
  -H "Authorization: Bearer $JUSTI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://example.com/hooks/justi","events":["launch.created","trade"],"description":"hold the line."}'

#Delete a webhook and its delivery log

DELETE/v1/webhooks/{id}
Key requiredRate class managedeleteWebhook

Parameters

NameTypeAbout
idrequiredpathstringUUID.
Response object
FieldTypeAbout
deletedrequiredboolean
Errors400 invalid_params401 unauthorized404 not_found429 rate_limited500 internal
curl -X DELETE "https://api.justi.fun/v1/webhooks/<id>" \
  -H "Authorization: Bearer $JUSTI_API_KEY"

#Delivery log, newest first

GET/v1/webhooks/{id}/deliveries
Key requiredRate class managelistWebhookDeliveries

Parameters

NameTypeAbout
idrequiredpathstringUUID.
limitqueryintegerPage size, 1 to 200. default 50
cursorquerystringnextCursor from the previous page. max 400 chars
Response object
FieldTypeAbout
deliveriesrequiredWebhookDelivery[]
idrequiredstring
eventIdrequiredstring | null
eventTyperequiredstring
statusrequired"pending" | "delivered" | "failed" | "cancelled"
attemptsrequiredinteger
nextAttemptAtrequiredinteger | nullUnix time in seconds.
lastStatusCoderequiredinteger | null
lastErrorrequiredstring | null
lastDurationMsrequiredinteger | null
createdAtrequiredintegerUnix time in seconds.
deliveredAtrequiredinteger | nullUnix time in seconds.
payloadrequiredobject
nextCursorrequiredstring | null
Errors400 invalid_params401 unauthorized404 not_found429 rate_limited500 internal
curl "https://api.justi.fun/v1/webhooks/<id>/deliveries?limit=20" \
  -H "Authorization: Bearer $JUSTI_API_KEY"

#Send one signed webhook.test delivery now

POST/v1/webhooks/{id}/test
Key requiredRate class managetestWebhook

Parameters

NameTypeAbout
idrequiredpathstringUUID.
Response object
FieldTypeAbout
deliveryIdrequiredstring
deliveredrequiredboolean
statusCoderequiredinteger | null
errorrequiredstring | null
durationMsrequiredinteger
Errors400 invalid_params401 unauthorized404 not_found429 rate_limited500 internal
curl -X POST "https://api.justi.fun/v1/webhooks/<id>/test" \
  -H "Authorization: Bearer $JUSTI_API_KEY"