Skip to main content

api reference (for developers): integrating with the postfun protocol

introduction: build on the cultural value layer

the postfun api provides programmatic access to our platform's data and functionality. we've designed it to be restful, robust, and intuitive, allowing developers to extend postfun's capabilities and build innovative tools on top of our cultural value protocol. whether you're creating analytics dashboards, automating trading strategies, or integrating postfun data into other applications, this reference will guide you.

disclaimer

this api reference details the interfaces used by our official clients (website and browser extension). while we encourage community development, please be aware that the api is subject to change. for high-volume use cases or to request direct access to authenticated endpoints (for non-browser environments), please contact our team via discord or partnerships@postfun.xyz to discuss your specific needs.

authentication: nostr-powered session management

postfun uses a hybrid authentication model: nostr for identity, jwt for session.

1. post /auth/login

  • purpose: authenticates a user based on a signed nostr event and issues a json web token (jwt) for subsequent api requests.

  • authentication: none (this is the login endpoint itself).

  • request method: post

  • endpoint: https://api.postfun.xyz/auth/login

  • request body (application/json): a nostr event object (kind 22242) signed by the user's nsec. the content field must contain a challenge string provided by a preceding request to the backend (not directly exposed here for simplicity, but assumed to be handled by the client's signevent flow).

    {
    "id": "e0e2...",
    "pubkey": "b47c...",
    "created_at": 1678886400,
    "kind": 22242,
    "tags": [],
    "content": "{\\"challenge\\": \\"your_unique_challenge_string\\", \\"purpose\\": \\"postfun login\\"}",
    "sig": "f1f2..."
    }
  • response (200 ok - application/json):

    {
    "token": "eyjhbgcioijsuzyijiuzi1niisinr5cci6ikpxicj9.eyj1c2vyx2lkijoiytyyyjc5mtitytc0yiotngrilotk0njqtotu2mthhnmy0otqziijwcgv5ijoiyjq3yzu1ndu0zjyymjawm2vlm2rkontjmxrjnhjkywm0nzikmxwcmnvzlmqxndq5otkxztm0mjaifq.signature"
    }
  • error responses:

    • 400 bad request: invalid event structure, missing fields.
    • 401 unauthorized: invalid signature, expired challenge, pubkey mismatch.

subsequent requests: once a jwt is obtained, it must be included in the authorization header for all protected endpoints: authorization: bearer <your_jwt_token>.

4.2. public data endpoints (no authentication required)

these endpoints provide read-only access to public postfun data.

1. get /stats

  • purpose: retrieve platform-wide aggregate statistics.
  • response (200 ok - application/json):
    {
    "total_assets_cumulative": 152345,
    "new_assets_24h": 345,
    "total_liquidity_sats": 123456789000,
    "liquidity_24h_sats": 5678901230,
    "total_volume_sats": 987654321000,
    "volume_24h_sats": 4321098760
    }

2. get /tweets

  • purpose: retrieve a paginated list of content pools based on various filters.
  • query parameters:
    • by (string, optional): sort order. accepted values: new (default), volume, level, gainers, losers.
    • page (integer, optional): page number for pagination (default: 1).
    • limit (integer, optional): number of results per page (default: 20, max: 100).
  • response (200 ok - application/json):
    [
    {
    "id": "a1b2c3d4e5f6g7h8",
    "tweet_id": "1678886400000000000",
    "tweet_username": "elonmusk",
    "tweet_content_snippet": "just bought a new dog. much wow. 🐕",
    "minter_npub": "npub1...",
    "current_price_sats_per_token": 0.00000125,
    "total_volume_sats": 50000000,
    "current_liquidity_sats": 8000000,
    "current_level": 2,
    "created_at": "2023-08-16T10:00:00Z"
    },
    {
    "id": "b2c3d4e5f6g7h8i9",
    "tweet_id": "1678886400000000001",
    "tweet_username": "balajis",
    "tweet_content_snippet": "the network state is emerging...",
    "minter_npub": "npub1...",
    "current_price_sats_per_token": 0.000005,
    "total_volume_sats": 150000000,
    "current_liquidity_sats": 15000000,
    "current_level": 3,
    "created_at": "2023-08-15T18:30:00Z"
    }
    ]

3. get /tweets/{tweetId}

  • purpose: retrieve detailed information for a specific content pool.
  • path parameters:
    • tweetId (string, required): the original x.com tweet id.
  • response (200 ok - application/json):
    {
    "id": "a1b2c3d4e5f6g7h8",
    "tweet_id": "1678886400000000000",
    "tweet_username": "elonmusk",
    "tweet_content_full": "just bought a new dog. much wow. to the moon and beyond with doge!",
    "minter_npub": "npub1l4t...",
    "creator_npub": "npub1n0x...",
    "current_price_sats_per_token": 0.00000125,
    "virtual_sats_reserve": 10500000.0,
    "token_reserve": 800000000.0,
    "bitcoin_reserve": 1500000,
    "total_volume_sats": 50000000,
    "current_level": 2,
    "next_level_target_sats": 10000000,
    "graduation_target_sats": 100000000,
    "status": "ACTIVE",
    "created_at": "2023-08-16T10:00:00Z",
    "holders_count": 125,
    "swaps_count": 560,
    "recent_swaps": [
    {
    "swap_id": "s_xyz789",
    "user_npub": "npub1a2b...",
    "direction": "BUY",
    "amount_in_sats": 100000,
    "amount_out_tokens": 8000000,
    "fee_sats": 10000,
    "timestamp": "2023-08-16T14:35:00Z"
    }
    ],
    "top_holders": [
    {"npub": "npub1c3d...", "amount_tokens": 500000000},
    {"npub": "npub1e4f...", "amount_tokens": 100000000}
    ]
    }

4. get /users/{twitterUsername}

  • purpose: retrieve public-facing information about a linked x.com user.
  • path parameters:
    • twitterUsername (string, required): the x.com username (e.g., elonmusk).
  • response (200 ok - application/json):
    {
    "twitter_username": "elonmusk",
    "is_linked_to_postfun": true,
    "minted_posts_count": 5,
    "total_earned_sats": 12345000
    }

4.3. authenticated endpoints (jwt required)

these endpoints require an authorization: bearer <jwt> header in the request.

1. get /me

  • purpose: retrieve the currently authenticated user's detailed private information.
  • response (200 ok - application/json):
    {
    "id": "a62b7912-a74b-4db6-9464-95618a6f4943",
    "npub": "npub1l4tm00000000000000000000000000000000000000000000000000000000",
    "balance_sats": 500000,
    "linked_twitter_username": "your_twitter_handle",
    "available_claim_sats": 15000,
    "holdings": [
    {"asset_id": "a1b2c3d4e5f6g7h8", "amount_tokens": 10000000, "current_value_sats": 12500},
    {"asset_id": "b2c3d4e5f6g7h8i9", "amount_tokens": 500000, "current_value_sats": 2500}
    ],
    "recent_deposits": [
    {"id": "d_123", "amount_sats": 100000, "status": "COMPLETED", "timestamp": "2023-08-15T12:00:00Z"}
    ],
    "recent_withdrawals": [
    {"id": "w_456", "amount_sats": 50000, "status": "COMPLETED", "timestamp": "2023-08-14T18:00:00Z"}
    ],
    "recent_swaps": [
    {"id": "s_789", "asset_id": "a1b2c3d4e5f6g7h8", "direction": "BUY", "amount_sats": 10000, "timestamp": "2023-08-16T14:35:00Z"}
    ]
    }

2. post /mints

  • purpose: create a new content pool for a specific tweet.
  • request body (application/json):
    {
    "tweet_id": "1678886400000000000"
    }
  • logic:
    1. authenticated user must have at least 1,000 sats in their balance.
    2. tweet must not have an existing postfun pool.
    3. a mint job is queued for asynchronous processing. the 1,000 sats fee is immediately deducted from the user's balance.
  • response (202 accepted): { "message": "mint job queued successfully. please check your dashboard for status." }

3. post /swaps

  • purpose: execute a buy or sell transaction for a content pool token.
  • request body (application/json):
    {
    "tweet_id": "1678886400000000000",
    "direction": "BUY",
    "amount_in_sats": 10000,
    "min_amount_out": 5000000
    }
  • logic:
    1. validate user's balance for amount_in_sats (for buy) or amount_in_tokens (for sell).
    2. ensure amount_in_sats is at least 1 sat (minimum input).
    3. a swap job is queued for asynchronous processing.
  • response (202 accepted): { "message": "swap job queued successfully. transaction pending." }

4. post /deposits

  • purpose: request a lightning network invoice to deposit sats into the user's custodial wallet.
  • request body (application/json):
    {
    "amount_sats": 50000,
    "memo": "deposit to postfun account"
    }
  • logic:
    1. checks if the user has too many pending deposit invoices. (limits concurrency for security/operational efficiency).
    2. requests a bolt11 invoice from the integrated lightning service.
    3. stores the invoice details (payment hash, expiry) in the deposits table with status='PENDING'.
  • response (200 ok): { "payment_request": "lnbc500n1p..." }

5. post /withdrawals/initiate

  • purpose: initiate a lightning network withdrawal by providing an invoice. returns fee information.
  • request body (application/json):
    {
    "invoice": "lnbc100u1p..."
    }
  • logic:
    1. decodes the provided bolt11 invoice.
    2. calculates the withdrawal fee: 0.5% of amount or 10 sats, whichever is higher.
    3. checks if the user's current balance_sats is sufficient to cover the withdrawal amount + fee.
  • response (200 ok):
    {
    "decoded_amount_sats": 10000,
    "calculated_fee_sats": 50,
    "total_sats_debited": 10050,
    "memo": "withdrawal from postfun"
    }

6. post /withdrawals/confirm

  • purpose: confirm and queue a lightning network withdrawal after user reviews fees.
  • request body (application/json):
    {
    "invoice": "lnbc100u1p..."
    }
  • logic:
    1. re-validates the invoice and user's balance.
    2. queues a withdrawal job for asynchronous processing.
  • response (202 accepted): { "message": "withdrawal job queued successfully. payment will be processed shortly." }

7. post /users/link-twitter

  • purpose: securely link an x.com account to the authenticated postfun user's npub.
  • request body (application/json):
    {
    "tls_notary_proof": { /* ... raw tls notary proof object ... */ }
    }
  • logic:
    1. receives the tls notary proof.
    2. uses an internal verifier to validate the proof (e.g., checks against x.com's certificate, proves user's session).
    3. if valid, extracts the x.com username/id from the proof.
    4. links the authenticated user_id to the twitter_users record, marking it as verified.
  • response (200 ok): { "message": "twitter account successfully linked.", "linked_username": "elonmusk" }

8. post /claims

  • purpose: for a content creator to claim their accumulated earned_sats (fees and bonuses) into their main balance_sats.
  • request body (application_json): {} (no specific body, as user is identified by jwt).
  • logic:
    1. verifies the authenticated user has a linked and verified x.com account with a non-zero earned_sats balance.
    2. queues a claim job. the worker will atomically transfer the earned_sats to balance_sats and reset earned_sats to zero.
  • response (202 accepted): { "message": "claim job queued successfully. funds will appear shortly." }