All guides

SDK and MCP integration

External-ID members, campaign storefronts, Discord linking, server inventory and member attributes, quest action and completion contracts.

Reviewed Oct 8, 2026View .md

SDK 0.4.0

Both standalone ProjectStorefronts created with MCP create_storefront and campaign storefronts are supported. ModuleStorefront IDs are a separate legacy namespace; use list_features IDs. create_storefront starts inactive: add_storefront_item, then set_feature_active. No Hub widget is required for SDK-only use. For campaign stores, add_storefront_item preserves draft ownership; manage_campaign accepts the root campaign ID and get/save/preview/publish/archive. Publishing applies dashboard readiness rules to all activities. list_features identifies standalone versus campaign and the owning child campaign ID plus owningCampaignId for the root campaign. get_storefront_items accepts storefront (ID or unique name). Negative resource amounts are prices, positive amounts are rewards; requirements carry holding limits, stock carries configured total limits. SDK reads include availableStock and canPurchase after balance and prior purchase checks.

External-ID member APIs

See sdk/member-data: Send member data from your backend. sessions.create and members.upsert accept username, email and project-defined customFields. Phone belongs in customFields.phone. No fields are created silently. Inventory, balances and idempotent consumption remain available through members.inventory, members.balances and members.consume.

Harbor-hosted Discord linking

Create sessions with {externalId,origin,returnUrl}; returnUrl must be a URL on the backend-approved origin, with no fragment or reserved harbor_link_* query parameters. It defaults to the origin root. For an unlinked Discord connect/server-join quest, action.kind is oauth and action.url is a short-lived Harbor link. Open it in the same tab. Harbor obtains Discord consent and returns to returnUrl with harbor_link_receipt (or harbor_link_error on cancellation/failure).

On return, call await client.social.finishFromRedirect() before reading quests, or press Check in the default Quest widget: quests.verify finishes the returned receipt first. Finish requires a current SDK session for the same project, member, and origin. Discord identities already owned by a different member are rejected; existing identities are never silently replaced or merged. The receipt is not a login session. A successful link does not itself prove guild membership. Refresh the quest: a server-join quest exposes its Discord invitation, and Check verifies current membership with Discord before recording completion. Users may need to accept the invitation before Check succeeds.

For explicit connect/reconnect UI use client.social.link('discord', returnUrl), navigate to its url, then finishFromRedirect on return. SDK 0.4.0 provides hosted Discord linking. Other provider OAuth, forms, uploads and wallet actions still use host onAction adapters; do not display a working connect button when the action has no hosted URL and no adapter.

Quest action values and verification

Possible action.kind values: oauth, link, form, projectForm, upload, tiktok, wallet, walletSolana, external, invite, keyphrase, verify, check. action can be null. URLs are optional; only open HTTP(S) URLs. Use action.label when present. oauth with a hosted URL opens Harbor; oauth without one requires an adapter. link/external/invite open their URL then recheck. keyphrase asks for text and passes keyPhrase. form/projectForm/upload/tiktok/wallet/walletSolana require their authenticated adapter. verify/check invoke quests.verify. Unknown future values must render an unsupported-action state.

quests.verify always returns an updated HarborQuest on success, not a boolean and not a reward receipt. Inspect isCompleted and canClaimNow; claim separately. ClickLink records the configured visit. DiscordJoin asks Discord for membership; missing identity returns identity_link_required, non-membership requirements_not_met, and provider failure provider_unavailable. ExternalCompletion is read-only in browser verification: only the trusted host backend records gameplay completion. Other affirmative types run Harbor's configured verifier; unmet requirements return ineligible and unimplemented verifiers return unsupported_action. All other types re-read stored events or live resource state without asserting browser-supplied completion. The returned quest can remain incomplete after a successful read. Disabled, archived, inaccessible or foreign-project quests are unavailable.

questTypeName values (case-sensitive): TwitterConnectAccount, DiscordConnectAccount, SteamConnectAccount, TwitchConnectAccount, EmailConnect, ReferFriend, RewardTrigger, CompleteQuests, TwitterFollow, TwitterRetweet, TwitterLike, TwitterComment, DiscordJoin, DiscordSendMessages, DiscordSendMessagesInChannel, ChallengeCompete, ResourceEarn, WagerWin, Tip, DiscordRoleEarn, BoxOpen, ResourceUse, GetTipped, TwitterQuote, TwitterReply, ResourceHave, FormCompletePaperform, TextInput, ClickLink, Nothing, RobloxBadgeHave, RobloxBadgesOwned, RobloxConnectAccount, EthereumWalletConnect, FormCompleteTallyIO, FormCompleteTypeform, ExternalCompletion, RobloxStarAsset, EpicConnectAccount, TwitchFollow, TwitchWatch, SteamProfilePublic, SteamGameAchievement, SteamGamePlaytime, SteamGameRecentPlaytime, FacebookConnectAccount, FacebookLikePage, FacebookSharePost, FacebookHashtagPost, TikTokConnectAccount, TikTokCreatePost, TikTokDirectPost, YoutubeConnectAccount, YoutubeSubscribe, YoutubeLike, YoutubeComment, SolanaWalletConnect, BeamableConnectAccount, BeamableConnectSteamAccount, ExternalIdentitySelfCreate, SteamGameLibraryCheck, SteamGameWishlistCheck, FormCompleteProjectForm, ImageUpload. These identify configuration, not a promise that an adapter or provider integration is enabled. Hosts should choose buttons from action rather than maintain a separate type-to-button mapping.

Completion, repeats, and errors

Use @harbor/server quests.complete -> POST /external/sdk/quests/complete for an ExternalCompletion quest and an external-ID member, with operationId. Create the member first through members.upsert or sessions.create. It records eligibility only; the member claims separately. The legacy /external/quest/complete endpoint uses a target identity and its existing contract; it is not a substitute for SDK durable operation receipts.

Completion codes: member_not_found (create member first), not_found (unknown/foreign quest), unsupported_action (not ExternalCompletion), outside_campaign_dates, ineligible (audience, lifecycle, group or other eligibility), already_completed (claimed non-repeatable quest), not_repeatable_yet (repeat interval has not reopened), invalid_request, idempotency_conflict. An already-completed but unclaimed quest remains successfully complete. A matching successful operationId always replays its original receipt, including across later repeat cycles; a new legitimate event needs a new operationId.

A Daily group label alone does not make an ExternalCompletion quest reset. Campaign Daily/Refreshing groups currently admit Nothing and ResourceHave actions; ExternalCompletion is not supported in those campaign structures. Campaign Daily cycles reopen after a claimed reward at 00:00 UTC, on the next eligible request. Standalone group display does not add a daily reset to each quest. ExternalCompletion repetition follows its own configured repeat rule and requires a fresh backend event and operationId after reopening; do not promise a daily gameplay reward based only on group type.

Capability discovery and refresh

harbor.capabilities() calls POST /external/sdk/capabilities with the project key. It returns apiVersion, minimumSdkVersion for these features, migrationsReady, and features. Check this before adopting 0.4.0 against an older API; an older deployment may return 404. Migration readiness checks required SDK operation, storefront purchase, custom-field storage tables, and native email provenance. Package version alone is not proof of server deployment.

Quest-claim receipts already list storefronts alongside inventory, quests, questGroups and leaderboards. SDK subscribers refresh these reads, including canPurchase. Backend completion does not change balances and affects only quests and groups. Server-side consumption returns affected categories; the host must refresh mounted browser consumers after server-side mutations (there is no push subscription).

Per-type verification reference

Every successful verify returns an updated HarborQuest. Completion and claim eligibility are separate fields. A successful recheck may remain incomplete. verify/check below is selected from the configured affirm metadata; the response action is authoritative. Provider verifiers require their configured identity and integration.

questTypeName action.kind quests.verify behavior
TwitterConnectAccount oauth Re-reads linked identity; this provider requires a host adapter.
DiscordConnectAccount oauth Requires Harbor-linked Discord, then records completion.
SteamConnectAccount oauth Re-reads linked identity; this provider requires a host adapter.
TwitchConnectAccount oauth Re-reads linked identity; this provider requires a host adapter.
EmailConnect verify/check Re-reads stored events or evaluated state; does not record a browser assertion.
ReferFriend verify/check Re-reads stored events or evaluated state; does not record a browser assertion.
RewardTrigger verify/check Re-reads stored events or evaluated state; does not record a browser assertion.
CompleteQuests verify/check Re-reads stored events or evaluated state; does not record a browser assertion.
TwitterFollow verify/check Runs configured affirmative verifier; unmet conditions return ineligible, unavailable implementations return unsupported_action.
TwitterRetweet verify/check Re-reads stored events or evaluated state; does not record a browser assertion.
TwitterLike verify/check Runs configured affirmative verifier; unmet conditions return ineligible, unavailable implementations return unsupported_action.
TwitterComment verify/check Runs configured affirmative verifier; unmet conditions return ineligible, unavailable implementations return unsupported_action.
DiscordJoin oauth before linking; invite/check after linking Verifies current Discord guild membership; records completion only on success.
DiscordSendMessages verify/check Re-reads stored events or evaluated state; does not record a browser assertion.
DiscordSendMessagesInChannel verify/check Re-reads stored events or evaluated state; does not record a browser assertion.
ChallengeCompete verify/check Re-reads stored events or evaluated state; does not record a browser assertion.
ResourceEarn verify/check Re-reads stored events or evaluated state; does not record a browser assertion.
WagerWin verify/check Re-reads stored events or evaluated state; does not record a browser assertion.
Tip verify/check Re-reads stored events or evaluated state; does not record a browser assertion.
DiscordRoleEarn verify/check Runs configured affirmative verifier; unmet conditions return ineligible, unavailable implementations return unsupported_action.
BoxOpen verify/check Re-reads stored events or evaluated state; does not record a browser assertion.
ResourceUse verify/check Re-reads stored events or evaluated state; does not record a browser assertion.
GetTipped verify/check Re-reads stored events or evaluated state; does not record a browser assertion.
TwitterQuote verify/check Re-reads stored events or evaluated state; does not record a browser assertion.
TwitterReply verify/check Re-reads stored events or evaluated state; does not record a browser assertion.
ResourceHave check Re-evaluates current resource ownership.
FormCompletePaperform form Re-reads recorded submission from the authenticated form adapter.
TextInput keyphrase Checks supplied keyPhrase; returns updated quest or ineligible.
ClickLink link Records completion of the configured visit.
Nothing check Re-reads eligibility; claim can be immediately available.
RobloxBadgeHave verify/check Re-reads stored events or evaluated state; does not record a browser assertion.
RobloxBadgesOwned verify/check Re-reads stored events or evaluated state; does not record a browser assertion.
RobloxConnectAccount oauth Re-reads linked identity; this provider requires a host adapter.
EthereumWalletConnect wallet Re-reads linked wallet state; requires an authenticated adapter.
FormCompleteTallyIO form Re-reads recorded submission from the authenticated form adapter.
FormCompleteTypeform form Re-reads recorded submission from the authenticated form adapter.
ExternalCompletion external/check Re-reads backend-recorded completion; browser cannot complete gameplay.
RobloxStarAsset verify/check Runs configured affirmative verifier; unmet conditions return ineligible, unavailable implementations return unsupported_action.
EpicConnectAccount oauth Re-reads linked identity; this provider requires a host adapter.
TwitchFollow verify/check Runs configured affirmative verifier; unmet conditions return ineligible, unavailable implementations return unsupported_action.
TwitchWatch verify/check Re-reads stored events or evaluated state; does not record a browser assertion.
SteamProfilePublic verify/check Runs configured affirmative verifier; unmet conditions return ineligible, unavailable implementations return unsupported_action.
SteamGameAchievement verify/check Runs configured affirmative verifier; unmet conditions return ineligible, unavailable implementations return unsupported_action.
SteamGamePlaytime verify/check Runs configured affirmative verifier; unmet conditions return ineligible, unavailable implementations return unsupported_action.
SteamGameRecentPlaytime verify/check Runs configured affirmative verifier; unmet conditions return ineligible, unavailable implementations return unsupported_action.
FacebookConnectAccount oauth Re-reads linked identity; this provider requires a host adapter.
FacebookLikePage verify/check Runs configured affirmative verifier; unmet conditions return ineligible, unavailable implementations return unsupported_action.
FacebookSharePost verify/check Runs configured affirmative verifier; unmet conditions return ineligible, unavailable implementations return unsupported_action.
FacebookHashtagPost verify/check Runs configured affirmative verifier; unmet conditions return ineligible, unavailable implementations return unsupported_action.
TikTokConnectAccount oauth Re-reads linked identity; this provider requires a host adapter.
TikTokCreatePost tiktok Runs configured verifier after authenticated posting adapter.
TikTokDirectPost tiktok Runs configured verifier after authenticated posting adapter.
YoutubeConnectAccount oauth Re-reads linked identity; this provider requires a host adapter.
YoutubeSubscribe verify/check Runs configured affirmative verifier; unmet conditions return ineligible, unavailable implementations return unsupported_action.
YoutubeLike verify/check Runs configured affirmative verifier; unmet conditions return ineligible, unavailable implementations return unsupported_action.
YoutubeComment verify/check Runs configured affirmative verifier; unmet conditions return ineligible, unavailable implementations return unsupported_action.
SolanaWalletConnect walletSolana Re-reads linked wallet state; requires an authenticated adapter.
BeamableConnectAccount verify/check Runs configured affirmative verifier; unmet conditions return ineligible, unavailable implementations return unsupported_action.
BeamableConnectSteamAccount verify/check Runs configured affirmative verifier; unmet conditions return ineligible, unavailable implementations return unsupported_action.
ExternalIdentitySelfCreate verify/check Runs configured affirmative verifier; unmet conditions return ineligible, unavailable implementations return unsupported_action.
SteamGameLibraryCheck verify/check Runs configured affirmative verifier; unmet conditions return ineligible, unavailable implementations return unsupported_action.
SteamGameWishlistCheck verify/check Runs configured affirmative verifier; unmet conditions return ineligible, unavailable implementations return unsupported_action.
FormCompleteProjectForm projectForm Re-reads recorded submission from the authenticated form adapter.
ImageUpload upload Runs configured verifier after an authenticated upload adapter supplies the submission.