API

Crowdfunding

Campaign-scoped helpers for crowdfunding integrators. The DAO bearer token must be issued for the DAO that owns the campaign in the path.

Returns a ready-to-sign `ton://transfer/...` URI that contributes the
given `amount` to the specified crowdfunding campaign.

Auth: the DAO bearer token's `dao_address` must match the DAO that
owns the campaign. Mismatches return 403.

Whitelist: for jetton campaigns `investor_address` is checked against
the campaign whitelist — non-whitelisted investors get 403
`not_whitelisted`. For native campaigns the check happens on-chain and
a non-whitelisted contribution bounces back to the sender.

{"openapi":"3.0.3","info":{"title":"XDAO TON API docs","version":"1.1.0"},"tags":[{"name":"Crowdfunding","description":"Campaign-scoped helpers for crowdfunding integrators. The DAO bearer token\nmust be issued for the DAO that owns the campaign in the path.\n"}],"servers":[{"url":"https://ton.xdao.app"}],"security":[{"DaoBearerAuth":[]}],"components":{"securitySchemes":{"DaoBearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"DAO service token in Bearer format.\nThe token must be valid, not revoked, and authorized for the same `dao_address` as in path.\n"}},"parameters":{"CampaignAddressPath":{"name":"address","in":"path","required":true,"description":"Crowdfunding master address. Any TON format is accepted (raw `0:hex`,\n`EQ.../UQ.../kQ.../0Q...`, base64-url or standard base64).\n","schema":{"type":"string"}},"ContributionAmountQuery":{"name":"amount","in":"query","required":true,"description":"Contribution amount in the smallest units of the campaign currency:\nnanoTON for native campaigns, jetton smallest-units (e.g. `1000000`\n= 1 USDT for 6-decimal USDT) for jetton campaigns. Must satisfy\n`minAmountPerUser <= amount <= maxAmountPerUser`.\n","schema":{"type":"string","pattern":"^[0-9]+$"}},"ContributionInvestorAddressQuery":{"name":"investor_address","in":"query","required":false,"description":"Investor's TON wallet address. Required for jetton campaigns — used\nto resolve the investor's jetton wallet via `get_wallet_address` on\nthe jetton master, and checked against the campaign whitelist when one\nis configured (non-whitelisted investors get 403 `not_whitelisted`).\nIgnored for native campaigns.\n","schema":{"type":"string"}},"ContributionQueryIDQuery":{"name":"query_id","in":"query","required":false,"description":"TON message `query_id` (uint64). Defaults to 0.","schema":{"type":"integer","format":"int64","minimum":0,"default":0}},"ContributionResponseAddressQuery":{"name":"response_address","in":"query","required":false,"description":"Optional override for `response_destination` in the jetton-transfer\nbody. Defaults to `investor_address` if absent. Has no effect on\nnative campaigns.\n","schema":{"type":"string"}}},"schemas":{"ContributionLink":{"type":"object","required":["isNative","campaignAddress","tonTransfer","details","warnings"],"properties":{"isNative":{"type":"boolean","description":"`true` for native TON campaigns, `false` for jetton campaigns."},"campaignAddress":{"type":"string","description":"Address that the `ton://transfer/` URI sends the message to (bounceable\nmainnet form). For jetton campaigns this is the *investor's* jetton\nwallet, **not** the campaign master.\n"},"tonTransfer":{"type":"object","required":["uri"],"properties":{"uri":{"type":"string","description":"Ready-to-sign `ton://transfer/{to}?amount={nanoton}&bin={base64url}`\nURI. Open it in a TON wallet (Tonkeeper et al.) to sign — no further\nmanipulation should be necessary.\n"}}},"details":{"type":"object","description":"Same payload as `tonTransfer.uri`, but pre-decoded for clients that\nprefer to construct the transaction themselves (e.g. TonConnect).\n","required":["to","valueNanoton","bodyBocBase64Url"],"properties":{"to":{"type":"string","description":"Destination address (bounceable mainnet)."},"valueNanoton":{"type":"string","description":"Message value in nanoTON as a decimal string."},"bodyBocBase64Url":{"type":"string","description":"Message body BoC, base64url-encoded (no padding)."}}},"miniAppLink":{"type":"string","description":"Optional Telegram Mini-App deeplink that opens this campaign in the\nXDAO bot — bonus convenience for integrators.\n"},"warnings":{"type":"array","description":"Non-fatal signals. The transaction is still served. Known values:\n- `whitelist_enforced_on_chain` — native campaigns only: the campaign\n  has a whitelist and the investor is unknown at link time. The\n  on-chain check rejects non-whitelisted contributions and the funds\n  bounce back to the sender. For jetton campaigns the whitelist is\n  checked by this endpoint instead (403 `not_whitelisted`).\n- `amount_exceeds_remaining_hardcap` — `amount > hardCap - totalAmount`.\n","items":{"type":"string"}}}},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}},"responses":{"BadRequest":{"description":"Request is malformed. Error codes used by `/contribution-link`:\n- `invalid_campaign_address` — campaign path segment cannot be parsed\n- `invalid_amount` — amount is not a positive integer, or outside\n  `[minAmountPerUser, maxAmountPerUser]`\n- `missing_investor_address` — jetton campaign called without\n  `investor_address`\n- `invalid_investor_address` / `invalid_response_address` — address\n  cannot be parsed\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UnauthorizedError":{"description":"Token is missing, invalid, expired, or revoked.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"Access denied. Error codes:\n- token does not own this campaign's DAO, or a non-DAO token was used\n- `not_whitelisted` — jetton campaigns only: the campaign has a whitelist\n  and `investor_address` is not in it. No link is issued because a\n  non-whitelisted jetton contribution would NOT be refunded on-chain.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"Requested resource was not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"Campaign is in a state that cannot accept contributions. Error codes:\n- `campaign_paused` — admin has paused the campaign\n- `campaign_inactive` — `status != active` (already successful, failed,\n  or emergency-withdrawn)\n- `campaign_ended` — `end_time` has passed\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"InternalError":{"description":"Internal server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"BadGateway":{"description":"Upstream blockchain RPC failure. Error codes:\n- `jetton_wallet_lookup_failed` — `get_wallet_address` against the\n  jetton master failed (liteserver issue, contract not deployed, etc.)\n- `jetton_resolver_unavailable` — the API instance was started without\n  a liteclient and cannot serve jetton campaigns\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/api/v1/crowdfunding/campaigns/{address}/contribution-link":{"get":{"tags":["Crowdfunding"],"summary":"Contribution deeplink","description":"Returns a ready-to-sign `ton://transfer/...` URI that contributes the\ngiven `amount` to the specified crowdfunding campaign.\n\nAuth: the DAO bearer token's `dao_address` must match the DAO that\nowns the campaign. Mismatches return 403.\n\nWhitelist: for jetton campaigns `investor_address` is validated against\nthe campaign whitelist — non-whitelisted investors receive 403\n`not_whitelisted`. For native campaigns the whitelist is enforced\non-chain and a non-whitelisted contribution bounces back to the sender\n(the response carries the `whitelist_enforced_on_chain` warning).\n","operationId":"getContributionLink","parameters":[{"$ref":"#/components/parameters/CampaignAddressPath"},{"$ref":"#/components/parameters/ContributionAmountQuery"},{"$ref":"#/components/parameters/ContributionInvestorAddressQuery"},{"$ref":"#/components/parameters/ContributionQueryIDQuery"},{"$ref":"#/components/parameters/ContributionResponseAddressQuery"}],"responses":{"200":{"description":"Contribution link payload","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContributionLink"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/BadGateway"}}}}}}

Returns a ready-to-sign `ton://transfer/...` URI for the **initial** LP
claim. This is the `request_claim_lp_tokens` message the investor sends to
their per-campaign Helper contract once the campaign is `successful`.

Auth: the DAO bearer token's `dao_address` must match the DAO that owns the
campaign. Mismatches return 403.

{"openapi":"3.0.3","info":{"title":"XDAO TON API docs","version":"1.1.0"},"tags":[{"name":"Crowdfunding","description":"Campaign-scoped helpers for crowdfunding integrators. The DAO bearer token\nmust be issued for the DAO that owns the campaign in the path.\n"}],"servers":[{"url":"https://ton.xdao.app"}],"security":[{"DaoBearerAuth":[]}],"components":{"securitySchemes":{"DaoBearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"DAO service token in Bearer format.\nThe token must be valid, not revoked, and authorized for the same `dao_address` as in path.\n"}},"parameters":{"CampaignAddressPath":{"name":"address","in":"path","required":true,"description":"Crowdfunding master address. Any TON format is accepted (raw `0:hex`,\n`EQ.../UQ.../kQ.../0Q...`, base64-url or standard base64).\n","schema":{"type":"string"}},"ClaimOwnerAddressQuery":{"name":"owner_address","in":"query","required":true,"description":"Investor wallet address claiming LP tokens. Any TON format is accepted\n(raw `0:hex`, `EQ.../UQ.../kQ.../0Q...`, base64-url or standard base64).\n","schema":{"type":"string"}},"ContributionQueryIDQuery":{"name":"query_id","in":"query","required":false,"description":"TON message `query_id` (uint64). Defaults to 0.","schema":{"type":"integer","format":"int64","minimum":0,"default":0}}},"schemas":{"ClaimLink":{"type":"object","description":"Ready-to-sign deeplink for an LP claim. \n","required":["kind","campaignAddress","tonTransfer","details","warnings"],"properties":{"kind":{"type":"string","description":"Which claim this link performs.","enum":["initial","vesting"]},"campaignAddress":{"type":"string","description":"Bounceable mainnet form of the crowdfunding master address."},"tonTransfer":{"type":"object","required":["uri"],"properties":{"uri":{"type":"string","description":"Ready-to-sign `ton://transfer/{to}?amount={nanoton}&bin={base64url}`\nURI. For `kind=initial` `to` is the Helper; for `kind=vesting` the\nVestingVault.\n"}}},"details":{"type":"object","description":"Same payload as `tonTransfer.uri`, pre-decoded for clients that prefer\nto construct the transaction themselves (e.g. TonConnect).\n","required":["to","valueNanoton","bodyBocBase64Url"],"properties":{"to":{"type":"string","description":"Destination address (bounceable mainnet) — Helper or VestingVault."},"valueNanoton":{"type":"string","description":"Message value in nanoTON as a decimal string."},"bodyBocBase64Url":{"type":"string","description":"Message body BoC, base64url-encoded (no padding)."}}},"vesting":{"type":"object","description":"Present only for `kind=vesting`. An **estimate** of the claimable LP and\nthe current vesting phase; the exact amount is computed on-chain by the\nvault at execution time.\n","required":["phase","claimableAmount","vestedAmount","totalLpAmount","claimedLpAmount"],"properties":{"phase":{"type":"string","description":"Vesting phase at the time of the request.","enum":["not_started","cliff","vesting","fully_vested","emergency"]},"claimableAmount":{"type":"string","description":"Estimated claimable LP (vested minus already claimed), smallest units."},"vestedAmount":{"type":"string","description":"Estimated total vested LP so far, smallest units."},"totalLpAmount":{"type":"string","description":"Total LP allocated to the investor, smallest units."},"claimedLpAmount":{"type":"string","description":"LP already claimed, smallest units."},"vestingEnd":{"type":"string","format":"date-time","description":"Computed end = vestingStart + vestingDuration."}}},"warnings":{"type":"array","description":"Non-fatal signals. The transaction is still served. Known values:\n- `nothing_claimable_now` — estimated claimable amount is 0 (e.g. before\n  TGE or between linear ticks). Signing it will fail the vault's\n  `nothing_to_claim` guard.\n","items":{"type":"string"}}}},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}},"responses":{"BadRequest":{"description":"Request is malformed. Error codes used by `/contribution-link`:\n- `invalid_campaign_address` — campaign path segment cannot be parsed\n- `invalid_amount` — amount is not a positive integer, or outside\n  `[minAmountPerUser, maxAmountPerUser]`\n- `missing_investor_address` — jetton campaign called without\n  `investor_address`\n- `invalid_investor_address` / `invalid_response_address` — address\n  cannot be parsed\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UnauthorizedError":{"description":"Token is missing, invalid, expired, or revoked.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"Token does not have access to this DAO or endpoint.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"Requested resource was not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"Campaign is in a state that cannot accept contributions. Error codes:\n- `campaign_paused` — admin has paused the campaign\n- `campaign_inactive` — `status != active` (already successful, failed,\n  or emergency-withdrawn)\n- `campaign_ended` — `end_time` has passed\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"InternalError":{"description":"Internal server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/api/v1/crowdfunding/campaigns/{address}/claim-link":{"get":{"tags":["Crowdfunding"],"summary":"LP claim deeplink","description":"Returns a ready-to-sign `ton://transfer/...` URI for the **initial** LP\nclaim. This is the `request_claim_lp_tokens` message the investor sends to\ntheir per-campaign Helper contract once the campaign is `successful`.\n\nAuth: the DAO bearer token's `dao_address` must match the DAO that owns the\ncampaign. Mismatches return 403.\n","operationId":"getClaimLink","parameters":[{"$ref":"#/components/parameters/CampaignAddressPath"},{"$ref":"#/components/parameters/ClaimOwnerAddressQuery"},{"$ref":"#/components/parameters/ContributionQueryIDQuery"}],"responses":{"200":{"description":"Claim link payload","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClaimLink"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"500":{"$ref":"#/components/responses/InternalError"}}}}}}

Returns a ready-to-sign `ton://transfer/...` URI for the `claim_vested`
message the investor sends to their per-campaign VestingVault to release the
currently vested-but-unclaimed LP.

Auth: the DAO bearer token's `dao_address` must match the DAO that owns the
campaign. Mismatches return 403.

{"openapi":"3.0.3","info":{"title":"XDAO TON API docs","version":"1.1.0"},"tags":[{"name":"Crowdfunding","description":"Campaign-scoped helpers for crowdfunding integrators. The DAO bearer token\nmust be issued for the DAO that owns the campaign in the path.\n"}],"servers":[{"url":"https://ton.xdao.app"}],"security":[{"DaoBearerAuth":[]}],"components":{"securitySchemes":{"DaoBearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"DAO service token in Bearer format.\nThe token must be valid, not revoked, and authorized for the same `dao_address` as in path.\n"}},"parameters":{"CampaignAddressPath":{"name":"address","in":"path","required":true,"description":"Crowdfunding master address. Any TON format is accepted (raw `0:hex`,\n`EQ.../UQ.../kQ.../0Q...`, base64-url or standard base64).\n","schema":{"type":"string"}},"ClaimOwnerAddressQuery":{"name":"owner_address","in":"query","required":true,"description":"Investor wallet address claiming LP tokens. Any TON format is accepted\n(raw `0:hex`, `EQ.../UQ.../kQ.../0Q...`, base64-url or standard base64).\n","schema":{"type":"string"}},"ContributionQueryIDQuery":{"name":"query_id","in":"query","required":false,"description":"TON message `query_id` (uint64). Defaults to 0.","schema":{"type":"integer","format":"int64","minimum":0,"default":0}}},"schemas":{"ClaimLink":{"type":"object","description":"Ready-to-sign deeplink for an LP claim. \n","required":["kind","campaignAddress","tonTransfer","details","warnings"],"properties":{"kind":{"type":"string","description":"Which claim this link performs.","enum":["initial","vesting"]},"campaignAddress":{"type":"string","description":"Bounceable mainnet form of the crowdfunding master address."},"tonTransfer":{"type":"object","required":["uri"],"properties":{"uri":{"type":"string","description":"Ready-to-sign `ton://transfer/{to}?amount={nanoton}&bin={base64url}`\nURI. For `kind=initial` `to` is the Helper; for `kind=vesting` the\nVestingVault.\n"}}},"details":{"type":"object","description":"Same payload as `tonTransfer.uri`, pre-decoded for clients that prefer\nto construct the transaction themselves (e.g. TonConnect).\n","required":["to","valueNanoton","bodyBocBase64Url"],"properties":{"to":{"type":"string","description":"Destination address (bounceable mainnet) — Helper or VestingVault."},"valueNanoton":{"type":"string","description":"Message value in nanoTON as a decimal string."},"bodyBocBase64Url":{"type":"string","description":"Message body BoC, base64url-encoded (no padding)."}}},"vesting":{"type":"object","description":"Present only for `kind=vesting`. An **estimate** of the claimable LP and\nthe current vesting phase; the exact amount is computed on-chain by the\nvault at execution time.\n","required":["phase","claimableAmount","vestedAmount","totalLpAmount","claimedLpAmount"],"properties":{"phase":{"type":"string","description":"Vesting phase at the time of the request.","enum":["not_started","cliff","vesting","fully_vested","emergency"]},"claimableAmount":{"type":"string","description":"Estimated claimable LP (vested minus already claimed), smallest units."},"vestedAmount":{"type":"string","description":"Estimated total vested LP so far, smallest units."},"totalLpAmount":{"type":"string","description":"Total LP allocated to the investor, smallest units."},"claimedLpAmount":{"type":"string","description":"LP already claimed, smallest units."},"vestingEnd":{"type":"string","format":"date-time","description":"Computed end = vestingStart + vestingDuration."}}},"warnings":{"type":"array","description":"Non-fatal signals. The transaction is still served. Known values:\n- `nothing_claimable_now` — estimated claimable amount is 0 (e.g. before\n  TGE or between linear ticks). Signing it will fail the vault's\n  `nothing_to_claim` guard.\n","items":{"type":"string"}}}},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}},"responses":{"BadRequest":{"description":"Request is malformed. Error codes used by `/contribution-link`:\n- `invalid_campaign_address` — campaign path segment cannot be parsed\n- `invalid_amount` — amount is not a positive integer, or outside\n  `[minAmountPerUser, maxAmountPerUser]`\n- `missing_investor_address` — jetton campaign called without\n  `investor_address`\n- `invalid_investor_address` / `invalid_response_address` — address\n  cannot be parsed\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UnauthorizedError":{"description":"Token is missing, invalid, expired, or revoked.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"Token does not have access to this DAO or endpoint.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"Requested resource was not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"Campaign is in a state that cannot accept contributions. Error codes:\n- `campaign_paused` — admin has paused the campaign\n- `campaign_inactive` — `status != active` (already successful, failed,\n  or emergency-withdrawn)\n- `campaign_ended` — `end_time` has passed\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"InternalError":{"description":"Internal server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/api/v1/crowdfunding/campaigns/{address}/vesting-claim-link":{"get":{"tags":["Crowdfunding"],"summary":"Vested LP claim deeplink","description":"Returns a ready-to-sign `ton://transfer/...` URI for the `claim_vested`\nmessage the investor sends to their per-campaign VestingVault to release the\ncurrently vested-but-unclaimed LP.\n\nAuth: the DAO bearer token's `dao_address` must match the DAO that owns the\ncampaign. Mismatches return 403.\n","operationId":"getVestingClaimLink","parameters":[{"$ref":"#/components/parameters/CampaignAddressPath"},{"$ref":"#/components/parameters/ClaimOwnerAddressQuery"},{"$ref":"#/components/parameters/ContributionQueryIDQuery"}],"responses":{"200":{"description":"Vesting claim link payload","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClaimLink"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"500":{"$ref":"#/components/responses/InternalError"}}}}}}

On this page