REST API reference
Base URL: https://hive.lnkbase.com/api/v2/publications/{pubId} or the shortcut /api/v2/publication. Authenticate with Authorization: Bearer <API key>. Lists return {data, next_cursor, has_more}. Rate limit 180 requests/minute per key. Machine-readable spec: openapi.json. Guide: API guide.
Publication
GET
/Get the publication this key belongs toStats
GET
/statsSummary: growth, engagement, revenue (plain-English summary included)Query: days Period in days (default 30)
GET
/stats/growthDaily subscriber growth and acquisition sourcesQuery: days Period in days (default 30)
GET
/stats/earningsEarnings report: orders, MRR, network earningsQuery: days Period in days (default 30)
Subscriptions
GET
/subscriptionsList subscribers (filter by status, tier, email, source, created range)Query: limit Page size 1-100 (default 50) · cursor Opaque cursor from next_cursor · status active|pending|unsubscribed|bounced|complained · tier free|premium · source Acquisition source · email Email contains · tag Has tag · created_after ISO date · created_before ISO date
GET
/subscriptions/:idGet a subscriber by id or email (includes tags and recent activity)POST
/subscriptionsCreate (or update) a subscriber — validated, double opt-in respectedBody: email string (required) · name string · utm_source string · utm_medium string · utm_campaign string · referral_code Referrer code · custom_fields object · tags string[] · list_ids string[] · tier free|premium · send_welcome_email boolean (default true) · reactivate_existing boolean · double_opt_override "on"|"off" (default: publication setting)
PATCH
/subscriptions/:idUpdate a subscriber (name, tier, status, custom fields, tags)Body: name string · email string · tier free|premium · status active|unsubscribed · custom_fields object (null value deletes a field) · add_tags string[] · remove_tags string[]
DELETE
/subscriptions/:idPermanently delete a subscriberCustom fields
GET
/subscriptions/:id/custom_fieldsRead a subscriber's custom field valuesPUT
/subscriptions/:id/custom_fieldsWrite custom field values on a subscriber (merge; null deletes)Body: custom_fields object {key: value}
GET
/custom_fieldsList custom field definitionsPOST
/custom_fieldsCreate a custom fieldBody: key string · label string · kind text|number|date|boolean|list · options string[] (for list)
PATCH
/custom_fields/:idUpdate a custom field label/optionsBody: label string · kind string · options string[]
DELETE
/custom_fields/:idDelete a custom field definitionTags
GET
/subscriptions/:id/tagsList a subscriber's tagsPOST
/subscriptions/:id/tagsAdd tags to a subscriberBody: tags string[]
DELETE
/subscriptions/:id/tags/:tagRemove a tag from a subscriberGET
/tagsAll tags with subscriber countsReferral program
GET
/subscriptions/:id/referralsA subscriber's referral stats, referred readers and rewards earnedGET
/referral_programRewards/milestones and program totalsGET
/referral_program/leaderboardTop referrersQuery: limit 1-100
Subscriber intelligence
GET
/subscriptions/:id/insightsValue score, lifetime value, purchase likelihood, churn risk and public signals for a subscriberGET
/insights/top_subscribersMost valuable subscribers (sort: value | clv | purchase | churn)Query: sort value|clv|purchase|churn · limit 1-100
GET
/insights/anomaliesUnusual changes in the last 24 hours vs the trailing 14 days (undismissed, last 7 days)GET
/insights/next_actionsSuggested next steps computed from your data, each with a dashboard linkSegments
GET
/segmentsList segmentsPOST
/segmentsCreate a segment from rules (dynamic or static)Body: name string · description string · kind dynamic|static · rules RuleGroup {op, rules:[{field, cmp, value}]} — see /docs/api#segments
GET
/segments/:idGet a segmentGET
/segments/:id/subscribersList every subscriber in a segment (cursor paginated)Query: limit 1-500 (default 100) · cursor next_cursor · status "all" to include inactive (dynamic segments)
POST
/segments/:id/recalculateRecalculate a segment nowDELETE
/segments/:idDelete a segmentLists
GET
/listsList newsletter lists with member countsPOST
/lists/:id/subscribersAdd a subscriber (id or email) to a listBody: subscriber_id string · email string
DELETE
/lists/:id/subscribers/:sidRemove a subscriber from a listAutomations
POST
/automationsCreate an automation (saved as a draft; activate=true publishes it and turns it on)Body: name string · description string · allow_reentry boolean · trigger {type, config, filter?} — types: signup, confirmed, welcome, tag_added, field_updated, list_subscribed, upgraded, downgraded, purchase, event_registered, survey_submitted, poll_voted, email_opened, link_clicked, referral_milestone, segment_entered, date_field, inactive, api, external_purchase, cart_abandoned, webhook · triggers [{type, config, filter?}] — several entry points (subscribers enter when any matches); replaces trigger · steps [{id?, type, config, yes?, no?}] — types: send_email, delay, wait_until, condition, ab_split, add_tag, remove_tag, update_field, add_to_list, remove_from_list, webhook, enroll, grant_premium, notify, unsubscribe, exit; send_email config: {subject, markdown | doc | blocks} · template string — start from a template key (GET /automation_templates); trigger/steps override it · activate boolean
PATCH
/automations/:idEdit an automation — trigger/steps go to the draft version; the live version keeps running until publishedBody: name string · description string · allow_reentry boolean · trigger {type, config, filter?} — types: signup, confirmed, welcome, tag_added, field_updated, list_subscribed, upgraded, downgraded, purchase, event_registered, survey_submitted, poll_voted, email_opened, link_clicked, referral_milestone, segment_entered, date_field, inactive, api, external_purchase, cart_abandoned, webhook · triggers [{type, config, filter?}] — several entry points (subscribers enter when any matches); replaces trigger · steps [{id?, type, config, yes?, no?}] — types: send_email, delay, wait_until, condition, ab_split, add_tag, remove_tag, update_field, add_to_list, remove_from_list, webhook, enroll, grant_premium, notify, unsubscribe, exit; send_email config: {subject, markdown | doc | blocks}
POST
/automations/:id/publishPublish the draft version (running journeys continue on the new version); activate=true also turns it onBody: activate boolean (default false)
POST
/automations/:id/pausePause an active automation (subscribers wait where they are)POST
/automations/:id/resumeResume a paused automationGET
/automationsList automations with journey countsGET
/automations/:idGet an automation with per-email statsPOST
/automations/:id/journeysEnroll a subscriber (id or email) into an automationBody: subscriber_id string · email string (created if new) · data object passed to the journey
GET
/automations/:id/journeysList journeys (per-subscriber progress)Query: limit Page size 1-100 (default 50) · cursor Opaque cursor from next_cursor
GET
/automation_templatesList the automation templates library (use the key as `template` when creating an automation)Query: category string
GET
/automations/:id/webhookGet the secret incoming-webhook URL of an automation (POST JSON with an email to it to subscribe + enroll)Posts
GET
/postsList posts (filter by status; expand=stats includes engagement)Query: limit Page size 1-100 (default 50) · cursor Opaque cursor from next_cursor · status draft|scheduled|published|archived · audience all|free|premium · expand stats,content
GET
/posts/:idGet a post with content, stats (opens, clicks, engagement) and provider breakdownGET
/posts/:id/statsEngagement stats for a post (unique, bot-filtered)GET
/posts/:id/htmlRendered HTML: email vs web, free vs premiumQuery: format email|web (default email) · audience free|premium (default free) · raw 1 = return text/html
POST
/postsCreate a draft post (blocks, HTML, or Markdown; optionally from a template)Body: title string · subtitle string · blocks Block[] (see Editor document) · html string (wrapped in an HTML block) · markdown string · template_id Start from this template (content is copied; its style stays linked) · email_subject string · preview_text string · platform email|web|both · audience all|free|premium · thumbnail_url string · seo object · segment_ids string[] · author_ids string[]
PATCH
/posts/:idUpdate a post (replacing content creates a version)Body: title string · subtitle string · blocks Block[] · html string · markdown string · email_subject string · preview_text string · slug string · platform string · audience string · seo object · segment_ids string[] · exclude_segment_ids string[] · ab_test object|null
POST
/posts/:id/contentAppend or prepend content blocksBody: position append|prepend (default append) · blocks Block[] · html string · markdown string
DELETE
/posts/:idDelete a postPOST
/subject-line/checkScore an email subject line (length, preview text, spam words, caps, emoji, personalization) with per-check adviceBody: subject string · preview_text string
Send API
POST
/posts/:id/sendPublish/send now or schedule (targeting + subject A/B test)Body: scheduled_at ISO datetime (omit to send now) · segment_ids string[] send to · exclude_segment_ids string[] · list_id string · audience all|free|premium · platform email|web|both · email_subject string · ab_test {subjects: string[2-5] | variants: [{subject?, preview_text?, from_name?, content: a|b}] (2-4), sample_pct, wait_minutes, metric: opens|clicks} · alt_markdown string — Variant B content for content tests · send_timing {mode: sto} (each subscriber at their usual open hour, within 24h) | {mode: timezone, local_time: "09:00"} | null
POST
/posts/:id/unscheduleCancel a scheduled post (back to draft)POST
/posts/:id/testSend a test email (optionally as a simulated subscriber for merge tags)Body: emails string[] (max 10) · subscriber_id Preview merge tags / dynamic content as this subscriber
GET
/posts/:id/sendsSend history for a postTemplates
GET
/templatesList templates (publication + built-in library)Query: kind post|welcome|automation|website_page|website_section|website_block
GET
/templates/:idGet a template with content and style tokensPOST
/templates/codedCreate a custom-coded template from a full HTML email (sanitized; {{unsubscribe_url}} added when missing)Body: name string · description string · html string (full HTML document or fragment; merge tags + {{unsubscribe_url}}, {{preferences_url}}, {{web_url}})
Snippets
GET
/snippetsList reusable content snippetsGET
/snippets/:idGet a snippet with its blocks. Insert `synced_block` into a post to keep it linked, or copy `content.blocks`POST
/snippetsCreate a snippet from editor blocksBody: name string · description string · blocks Block[] (editor document blocks)
PATCH
/snippets/:idUpdate a snippet (synced copies everywhere pick up the change)Body: name string · description string · blocks Block[]
DELETE
/snippets/:idDelete a snippet (synced references render nothing afterwards)Commerce
GET
/products/recommendationsPersonalized product recommendations for a subscriber (what the recommended_products block would show)Query: subscriber_id Subscriber id (omit for bestsellers) · limit 1-6 (default 3)
GET
/ordersList orders (subscriptions, products, tickets…)Query: limit Page size 1-100 (default 50) · cursor Opaque cursor from next_cursor · kind subscription|product|ticket|gift|group|sponsorship|replay · status pending|paid|refunded|failed
GET
/productsList digital products with sales totalsTiers
GET
/tiersList paid tiersPOST
/tiersCreate a paid tierBody: name string · description string · currency usd|eur|… · monthly_cents int · annual_cents int · trial_days int · paid_trial_cents int · benefits string[]
PATCH
/tiers/:idUpdate a tier (status active|paused pauses/resumes sales)Body: name string · monthly_cents int · annual_cents int · status active|paused · benefits string[]
DELETE
/tiers/:idArchive a tierGET
/premium_subscriptionsList premium subscriptionsQuery: limit Page size 1-100 (default 50) · cursor Opaque cursor from next_cursor · status trialing|active|paused|past_due|canceled
POST
/subscriptions/:id/premiumGrant premium (comp) to a subscriberBody: tier_id string (default: first active tier) · interval month|year · days int (omit for no expiry)
DELETE
/subscriptions/:id/premiumCancel premium for a subscriberPolls & surveys
GET
/pollsList polls with vote totalsGET
/polls/:idPoll results (live aggregate)POST
/pollsCreate a poll (or a trivia question) to embed in postsBody: question string · options string[] (2-10) · purpose general|topic|product|pricing · kind standard|trivia · correct_option trivia: 1-based option index · explanation trivia: shown after voting
PATCH
/polls/:idClose or reopen a pollBody: closed boolean
GET
/polls/:id/responsesIndividual poll votes tied to subscribersQuery: limit Page size 1-100 (default 50) · cursor Opaque cursor from next_cursor
GET
/surveysList surveys with response countsGET
/surveys/:idSurvey with per-question aggregate resultsPOST
/surveysCreate a surveyBody: title string · description string · questions [{label, kind: short|long|single|multi|dropdown|rating|nps|date, options?, required?}] · thank_you string · status active|closed
PATCH
/surveys/:idUpdate a survey (title, description, thank-you message, status, questions)Body: title string · description string · questions array (replaces all) · thank_you string · status active|closed
GET
/surveys/:id/responsesIndividual survey responsesQuery: limit Page size 1-100 (default 50) · cursor Opaque cursor from next_cursor
Webhooks
GET
/webhooksList webhook endpointsPOST
/webhooksCreate a webhook endpoint (returns the signing secret once)Body: url string · events string[] (default ["*"]) · description string · version payload version (default: latest — see GET /webhooks/versions)
GET
/webhooks/versionsAvailable payload versions with changelogPATCH
/webhooks/:idUpdate an endpoint (url, events, description, active, or upgrade/downgrade its payload version)Body: url string · events string[] · description string · active boolean · version payload version
DELETE
/webhooks/:idDelete a webhook endpointGET
/webhooks/:id/deliveriesDelivery log for an endpoint (expand=attempts includes every attempt)Query: limit Page size 1-100 (default 50) · cursor Opaque cursor from next_cursor · expand attempts
GET
/webhooks/:id/deliveries/:didOne delivery: payload and every attempt (status code, response, latency)Podcasts
GET
/podcastsList podcast showsGET
/podcasts/:id/episodesList episodes with download countsQuery: limit Page size 1-100 (default 50) · cursor Opaque cursor from next_cursor
Transactional
POST
/transactional/sendSend one transactional email (template or subject + html/markdown/text) with merge vars; idempotent with idempotency_keyBody: to Recipient email (required) · template_id Template id or slug · subject Subject (required without a template) · html HTML body · markdown Markdown body · text Plain-text body · merge_vars Object; {{key}} / {{nested.key}} in subject and body · reply_to Reply-To address · from_name Sender name override · attachments [{filename, content (base64), type}] — 5 MB total · tags string[] for searching the log · idempotency_key Repeat-safe key; a repeat returns the original message · track boolean — open/click tracking (default false)
GET
/transactional/messagesSearch the transactional message logQuery: q Recipient, subject or message id · status sent|simulated|failed|suppressed|bounced|complained · tag Tag · limit 1-200 (default 50) · before ISO date: older than
GET
/transactional/messages/:idOne transactional message with status, opens and clicksGET
/transactional/templatesList transactional templatesPOST
/transactional/templatesCreate or update (pass id) a transactional templateBody: id Existing template id (update) · name Name · slug template_id used in send calls · subject Subject with {{merge}} tags · body Markdown or HTML · format markdown|html