{"openapi":"3.1.0","info":{"title":"AdQuest partner API","version":"1.0.0","description":"The routes your app and backend call. Guides: https://dashboard.adquest.io/docs"},"servers":[{"url":"https://api.adquest.batriders.ir/api/v1"}],"paths":{"/platform/fulfillments/{fulfillmentId}/acknowledge":{"parameters":[{"name":"fulfillmentId","in":"path","required":true,"description":"Fulfillment ID from the webhook.","schema":{"type":"integer"},"example":12}],"post":{"tags":["Platform"],"operationId":"acknowledgePlatformFulfillment","summary":"Acknowledge a fulfillment","description":"Confirms that you delivered a reward announced by a fulfillment.requested webhook. Repeating the same external_reference is safe.","security":[{"PlatformKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"external_reference":{"type":"string","maxLength":190,"description":"Your own reference for the delivered reward. Max 190 characters.","example":"order-8841"}},"required":["external_reference"]}}}},"responses":{"200":{"description":"The fulfillment was acknowledged. data has id, kind, status, amount, asset, external_reference, last_failure_reason, acknowledged_at, quest_completion_id, lottery_claim_id and created_at.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope"}}}},"404":{"description":"The fulfillment was not found."},"409":{"description":"FULFILLMENT_CONFLICT. It was already acknowledged with a different external_reference."}},"x-docs":{"page":"api/server-routes","id":"acknowledge-fulfillment","order":1}}},"/platform/participant-sessions":{"post":{"tags":["Platform delivery"],"operationId":"createParticipantSession","summary":"Create a participant session using the host-selected SDK language","description":"Creates a short-lived participant token for one user of your platform. Call this from your backend only, after you have verified who the user is.","security":[{"PlatformSecret":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"external_user_id":{"type":"string","maxLength":255,"description":"Your own stable ID for the user. Max 255 characters.","example":"player-42"},"display_name":{"type":"string","maxLength":255,"description":"Name shown to the participant. Max 255 characters.","example":"Ada"},"username":{"type":"string","maxLength":255,"description":"Max 255 characters. Used by profile quests."},"email":{"type":"string","maxLength":255,"description":"Max 255 characters. Used by profile quests and for the winner email."},"phone":{"type":"string","maxLength":32,"description":"Max 32 characters. Used by profile quests."},"avatar_url":{"type":"string","maxLength":2048,"description":"Max 2048 characters."},"locale":{"type":"string","description":"Language of the widget: en or fa, region tags such as en-US accepted. Invalid values fall back to English and are never rejected.","example":"en-US"},"country_code":{"type":"string","description":"Participant’s reward country as an ISO 3166-1 alpha-2 code, such as IR. Null or empty clears it. An unassigned code returns 422.","example":"IR"},"timezone":{"type":"string","maxLength":64,"description":"IANA timezone. Max 64 characters."},"host_context":{"type":"object","description":"Version 1 host profile context. Max 8192 encoded bytes."},"location":{"type":"object","description":"Location permission context, such as {\"status\":\"granted\",\"latitude\":35.6892,\"longitude\":51.389}."},"push":{"type":"object","description":"Push permission context, such as {\"status\":\"granted\",\"token\":\"push-token\"}."},"capabilities":{"type":"object","description":"Permission map. Each of profile, location, push and custom is granted, denied or unavailable."}},"required":["external_user_id"]}}}},"responses":{"200":{"description":"The session was created. Return participant_token and its expiry to that user’s frontend. The token lasts 15 minutes.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Envelope"},{"type":"object","properties":{"data":{"$ref":"#/components/schemas/ParticipantSession"}}}]},"example":{"status":true,"code":"OK","message":"Participant session created","data":{"participant_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.EXAMPLE.SIGNATURE","expires_at":"2026-10-24T12:49:08+00:00","expires_in":900,"platform_user":{"id":1,"external_user_id":"player-42","display_name":"Ada","avatar_url":null,"country_code":null,"country_name":null,"eligibility_country_code":"IR","eligibility_country_source":"platform"},"eligibility":{"locale":"en","timezone":null,"location_permission":"unavailable","push_permission":"unavailable","profile_permission":"unavailable","has_display_name":true,"has_avatar":false,"has_username":false,"has_locale":true,"has_timezone":false,"has_email":false,"has_phone":false,"completeness_percent":28},"locale_resolution":{"requested":"en","effective":"en","source":"host"}}}}}},"401":{"description":"The Secret Key is missing or wrong.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"status":false,"code":"PARTICIPANT_AUTH_REQUIRED","message":"Platform secret is required","data":{"code":"PARTICIPANT_AUTH_REQUIRED"}}}}},"422":{"description":"A field failed validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"status":false,"code":"VALIDATION_ERROR","message":"country_code must be an assigned ISO 3166-1 alpha-2 code","data":null}}}},"429":{"description":"Too many session requests. Back off and retry later.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlainEnvelope"},"example":{"status":false,"message":"Too many session requests","data":{"code":"RATE_LIMITED"}}}}}},"x-docs":{"page":"api/participant-sessions","id":"create-participant-session","order":0},"x-example-verified":true}},"/platform/quests":{"get":{"tags":["Platform delivery"],"operationId":"listPlatformQuests","summary":"List quests","description":"Returns the quests this platform can show, newest first. Pass external_user_id to mark the ones the user has already completed.","security":[{"PlatformKey":[]}],"parameters":[{"name":"external_user_id","in":"query","required":false,"description":"Your ID for the user. Sets completed on each quest.","schema":{"type":"string"}},{"name":"X-AdQuest-Participant-Token","in":"header","required":false,"description":"The participant token. Optional here. It adds per-user state such as completed.","schema":{"type":"string"},"example":"PARTICIPANT_TOKEN"}],"responses":{"200":{"description":"The list of quests. effective_locale is the language the quests were chosen for.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Envelope"},{"type":"object","properties":{"effective_locale":{"type":"string"},"data":{"type":"array","items":{"$ref":"#/components/schemas/Quest"}}}}]},"example":{"status":true,"code":"OK","message":"Platform quests","effective_locale":"en","data":[{"id":23,"name":"Watch our intro video","type":"video","type_name":"Video","visibility":"private","platform_id":7,"token_reward":10,"token_reward_total":12,"gift_reward":null,"gift_reward_expires_at":null,"status":"active","screen":"watch","content":"https://cdn.example.com/intro.mp4","cover":"https://cdn.example.com/cover.jpg","redirect_url":null,"display_duration":null,"start_date":"2026-01-01","end_date":"2026-03-01","completed":false,"completion_condition":{"requires_timer":true,"requires_question":true,"requires_click":false,"requires_event":false,"requires_profile":false,"requires_referral":false,"requires_streak":false,"requires_custom":false,"answer_mode":null},"event_rule":null,"profile_rule":null,"referral_rule":null,"streak_rule":null,"custom_rule":null,"media":{"type":"video","url":"https://cdn.example.com/intro.mp4","cover":"https://cdn.example.com/cover.jpg"},"proof":{"type":"watch_answer","version":1,"media":"video","timed":true}}]}}}}},"x-docs":{"page":"api/quests","id":"list-quests","order":0}}},"/platform/quests/{questId}":{"parameters":[{"name":"questId","in":"path","required":true,"description":"Quest ID.","schema":{"type":"integer"},"example":23}],"get":{"tags":["Platform delivery"],"operationId":"getPlatformQuest","summary":"Get a quest","description":"Returns one quest.","security":[{"PlatformKey":[]}],"parameters":[{"name":"external_user_id","in":"query","required":false,"description":"Your ID for the user. Sets completed.","schema":{"type":"string"}},{"name":"X-AdQuest-Participant-Token","in":"header","required":false,"description":"The participant token. Optional here. It adds per-user state such as completed.","schema":{"type":"string"},"example":"PARTICIPANT_TOKEN"}],"responses":{"200":{"description":"The quest.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Envelope"},{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Quest"}}}]},"example":{"status":true,"code":"OK","message":"Quest details","data":{"id":23,"name":"Watch our intro video","type":"video","type_name":"Video","visibility":"private","platform_id":7,"token_reward":10,"token_reward_total":12,"reward_unit":null,"reward_is_max":true,"gift_reward":null,"gift_reward_expires_at":null,"status":"active","screen":"watch","content":"https://cdn.example.com/intro.mp4","cover":"https://cdn.example.com/cover.jpg","redirect_url":null,"display_duration":null,"start_date":"2026-01-01","end_date":"2026-03-01","completed":false,"completion_condition":{"requires_timer":true,"requires_question":true,"requires_click":false,"requires_event":false,"requires_profile":false,"requires_referral":false,"requires_streak":false,"requires_custom":false,"answer_mode":null},"event_rule":null,"profile_rule":null,"referral_rule":null,"streak_rule":null,"custom_rule":null,"media":{"type":"video","url":"https://cdn.example.com/intro.mp4","cover":"https://cdn.example.com/cover.jpg"},"proof":{"type":"watch_answer","version":1,"media":"video","timed":true}}}}}},"404":{"description":"The quest does not exist or is not available to this platform.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"status":false,"code":"QUEST_NOT_FOUND","message":"Quest not found","data":null}}}}},"x-docs":{"page":"api/quests","id":"get-quest","order":1},"x-example-verified":true}},"/platform/quests/{questId}/start":{"parameters":[{"name":"questId","in":"path","required":true,"description":"Quest ID.","schema":{"type":"integer"},"example":23}],"post":{"tags":["Platform delivery"],"operationId":"startPlatformQuest","summary":"Start a quest","description":"Starts a quest for a participant. For timed quests (video and banner) the response tells you how long the participant has to watch.","security":[{"PlatformKey":[],"ParticipantToken":[]}],"parameters":[{"name":"X-AdQuest-Participant-Token","in":"header","required":true,"description":"The participant token minted by your backend (POST /platform/participant-sessions). Required: it is the participant's identity. A body external_user_id is optional and must match it (403 PARTICIPANT_MISMATCH); a missing or invalid token is 401 PARTICIPANT_AUTH_REQUIRED.","schema":{"type":"string"},"example":"PARTICIPANT_TOKEN"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"external_user_id":{"type":"string","maxLength":255,"description":"Your ID for the user. Max 255 characters.","example":"player-42"},"watch_duration":{"type":"integer","description":"Seconds, 1 to 600."}},"required":["external_user_id"]}}}},"responses":{"200":{"description":"The quest was started. display_duration and expires_in (seconds) are included for video and banner quests only. Also returned with this status: the participant already completed this quest. status is false and data.completed is true. Do not grant a reward.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope"},"example":{"status":true,"code":"OK","message":"Quest started","data":{"quest_id":23,"display_duration":15,"expires_in":15}}}}},"404":{"description":"QUEST_NOT_FOUND. The quest does not exist or is not available."},"422":{"description":"The quest budget is used up. data.code is QUEST_BUDGET_EXHAUSTED. Other 422 responses are validation errors."}},"x-docs":{"page":"api/quests","id":"start-quest","order":2}}},"/platform/quests/{questId}/click":{"parameters":[{"name":"questId","in":"path","required":true,"description":"Quest ID.","schema":{"type":"integer"},"example":23}],"post":{"tags":["Platform delivery"],"operationId":"clickPlatformQuest","summary":"Record a click","description":"Records that the participant clicked the quest’s media. Send it only when a real click happens.","security":[{"PlatformKey":[],"ParticipantToken":[]}],"parameters":[{"name":"X-AdQuest-Participant-Token","in":"header","required":true,"description":"The participant token minted by your backend (POST /platform/participant-sessions). Required: it is the participant's identity. A body external_user_id is optional and must match it (403 PARTICIPANT_MISMATCH); a missing or invalid token is 401 PARTICIPANT_AUTH_REQUIRED.","schema":{"type":"string"},"example":"PARTICIPANT_TOKEN"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"external_user_id":{"type":"string","maxLength":255,"description":"Your ID for the user. Max 255 characters.","example":"player-42"}},"required":["external_user_id"]}}}},"responses":{"200":{"description":"The click was recorded. Also returned with this status: the participant already completed this quest. status is false and data.completed is true.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope"},"example":{"status":true,"code":"OK","message":"Quest click recorded","data":{"quest_id":23,"clicked_at":"2026-01-01T12:00:00.000Z"}}}}},"404":{"description":"QUEST_NOT_FOUND."}},"x-docs":{"page":"api/quests","id":"click-quest","order":3}}},"/platform/quests/{questId}/view":{"parameters":[{"name":"questId","in":"path","required":true,"description":"Quest ID.","schema":{"type":"integer"},"example":23}],"post":{"tags":["Platform delivery"],"operationId":"viewPlatformQuest","summary":"Record that a participant saw a quest. First viewed_at sticks.","description":"Records that the participant saw the quest. The first view is kept. Later calls keep the same viewed_at.","security":[{"PlatformKey":[],"ParticipantToken":[]}],"parameters":[{"name":"X-AdQuest-Participant-Token","in":"header","required":true,"description":"The participant token minted by your backend (POST /platform/participant-sessions). Required: it is the participant's identity. A body external_user_id is optional and must match it (403 PARTICIPANT_MISMATCH); a missing or invalid token is 401 PARTICIPANT_AUTH_REQUIRED.","schema":{"type":"string"},"example":"PARTICIPANT_TOKEN"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"external_user_id":{"type":"string","maxLength":255,"description":"Your ID for the user. Max 255 characters.","example":"player-42"}},"required":["external_user_id"]}}}},"responses":{"200":{"description":"The view was recorded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope"},"example":{"status":true,"code":"OK","message":"Quest view recorded","data":{"quest_id":23}}}}},"404":{"description":"QUEST_NOT_FOUND."},"422":{"description":"A field failed validation, for example a missing external_user_id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"status":false,"code":"VALIDATION_ERROR","message":"The external user id field is required.","data":{"external_user_id":["The external user id field is required."]}}}}}},"x-docs":{"page":"api/quests","id":"view-quest","order":4}}},"/platform/quests/{questId}/progress":{"parameters":[{"name":"questId","in":"path","required":true,"description":"Quest ID.","schema":{"type":"integer"},"example":23}],"post":{"tags":["Platform delivery"],"operationId":"progressPlatformQuest","summary":"Record watch progress","description":"Records watch evidence for a timed quest. Send it while the participant is really watching.","security":[{"PlatformKey":[],"ParticipantToken":[]}],"parameters":[{"name":"X-AdQuest-Participant-Token","in":"header","required":true,"description":"The participant token minted by your backend (POST /platform/participant-sessions). Required: it is the participant's identity. A body external_user_id is optional and must match it (403 PARTICIPANT_MISMATCH); a missing or invalid token is 401 PARTICIPANT_AUTH_REQUIRED.","schema":{"type":"string"},"example":"PARTICIPANT_TOKEN"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"external_user_id":{"type":"string","maxLength":255,"description":"Your ID for the user. Max 255 characters.","example":"player-42"},"watch_duration":{"type":"number","description":"Seconds watched so far.","example":8}},"required":["external_user_id"]}}}},"responses":{"200":{"description":"The progress was recorded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope"},"example":{"status":true,"code":"OK","message":"Quest progress recorded","data":{"quest_id":23,"elapsed_seconds":8,"required_seconds":15}}}}},"404":{"description":"QUEST_NOT_FOUND. The quest does not exist or is not available."},"422":{"description":"The evidence was rejected. data.reason says why. If the quest is already completed, data.completed is true."}},"x-docs":{"page":"api/quest-actions","id":"record-progress","order":1},"x-example-verified":true}},"/platform/quests/{questId}/visit":{"parameters":[{"name":"questId","in":"path","required":true,"description":"Quest ID.","schema":{"type":"integer"},"example":23}],"get":{"tags":["Platform delivery"],"operationId":"visitPlatformQuest","summary":"Attribute a visit","description":"Records that the participant followed the link of a visit quest and returns the destination. Visit quests only.","security":[{"PlatformKey":[],"ParticipantToken":[]}],"parameters":[{"name":"external_user_id","in":"query","required":true,"description":"Your ID for the user. May also be sent in the body.","schema":{"type":"string"}},{"name":"referrer_external_user_id","in":"query","required":false,"description":"The user who shared the link, if any.","schema":{"type":"string"}},{"name":"redirect","in":"query","required":false,"description":"Send 1 to receive an HTTP redirect to the destination instead of JSON.","schema":{"type":"string"}},{"name":"X-AdQuest-Participant-Token","in":"header","required":true,"description":"The participant token minted by your backend (POST /platform/participant-sessions). Required: it is the participant's identity. A body external_user_id is optional and must match it (403 PARTICIPANT_MISMATCH); a missing or invalid token is 401 PARTICIPANT_AUTH_REQUIRED.","schema":{"type":"string"},"example":"PARTICIPANT_TOKEN"}],"responses":{"200":{"description":"The visit was attributed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope"},"example":{"status":true,"code":"OK","message":"Visit attributed","data":{"quest_id":23,"redirect_url":"https://example.com/offer","clicked_at":"2026-01-01T12:00:00.000Z"}}}}},"400":{"description":"NOT_VISIT_QUEST. The quest is not a visit quest."},"401":{"description":"PARTICIPANT_AUTH_REQUIRED. external_user_id is missing."},"404":{"description":"QUEST_NOT_FOUND. The quest does not exist or is not available."}},"x-docs":{"page":"api/quest-actions","id":"visit","order":3}}},"/platform/quests/{questId}/events":{"parameters":[{"name":"questId","in":"path","required":true,"description":"Quest ID.","schema":{"type":"integer"},"example":23}],"post":{"tags":["Platform delivery"],"operationId":"ingestPlatformQuestEvent","summary":"Send a verified event","description":"Sends an event your app or server observed, such as a purchase, for event and custom quests.","security":[{"PlatformKey":[],"ParticipantToken":[]}],"parameters":[{"name":"X-AdQuest-Participant-Token","in":"header","required":true,"description":"The participant token minted by your backend (POST /platform/participant-sessions). Required: it is the participant's identity. A body external_user_id is optional and must match it (403 PARTICIPANT_MISMATCH); a missing or invalid token is 401 PARTICIPANT_AUTH_REQUIRED.","schema":{"type":"string"},"example":"PARTICIPANT_TOKEN"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"external_user_id":{"type":"string","description":"Your ID for the user. May also be sent as a query parameter.","example":"player-42"},"event_id":{"type":"string","description":"Your unique ID for the event. Sending the same ID again returns duplicate: true.","example":"evt_1001"},"event_name":{"type":"string","description":"The event the quest is waiting for.","example":"purchase_completed"},"source":{"type":"string","description":"Where the event came from. Defaults to sdk."},"timestamp":{"type":"string","description":"When the event happened."},"signature":{"type":"string","description":"Signature for server-signed events."},"delivery_token":{"type":"string","description":"Token from the quest start. May instead be sent as the X-AdQuest-Delivery-Token header."}},"required":["external_user_id","event_id","event_name"]}}}},"responses":{"200":{"description":"The event was accepted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope"},"example":{"status":true,"code":"OK","message":"Event accepted","data":{"quest_id":23,"event_id":"evt_1001","event_name":"purchase_completed","duplicate":false,"verified_at":"2026-01-01T12:00:00.000Z"}}}}},"401":{"description":"PARTICIPANT_AUTH_REQUIRED. external_user_id is missing."},"404":{"description":"QUEST_NOT_FOUND. The quest does not exist or is not available."},"422":{"description":"The event was rejected. data.code says why."}},"x-docs":{"page":"api/quest-actions","id":"send-event","order":4}}},"/platform/quests/{questId}/question":{"parameters":[{"name":"questId","in":"path","required":true,"description":"Quest ID.","schema":{"type":"integer"},"example":23}],"get":{"tags":["Platform delivery"],"operationId":"getPlatformQuestQuestion","summary":"Get the question","description":"Returns the question and its options for a video, banner or survey quest. The quest must have been started for this user.","security":[{"PlatformKey":[],"ParticipantToken":[]}],"parameters":[{"name":"external_user_id","in":"query","required":true,"description":"Your ID for the user.","schema":{"type":"string"}},{"name":"X-AdQuest-Participant-Token","in":"header","required":true,"description":"The participant token minted by your backend (POST /platform/participant-sessions). Required: it is the participant's identity. A body external_user_id is optional and must match it (403 PARTICIPANT_MISMATCH); a missing or invalid token is 401 PARTICIPANT_AUTH_REQUIRED.","schema":{"type":"string"},"example":"PARTICIPANT_TOKEN"}],"responses":{"200":{"description":"The first question, plus questions for surveys with several. Also returned with this status: status is false when external_user_id is missing, the quest was not started or its timer expired, or the quest has no question. The message says which.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope"},"example":{"status":true,"code":"OK","message":"Quest question","data":{"id":5,"title":"What did the video explain?","input_type":"choice","is_required":true,"options":[{"id":11,"title":"How to start"},{"id":12,"title":"How to pay"}],"questions":[]}}}}},"404":{"description":"QUEST_NOT_FOUND. The quest does not exist or is not available."}},"x-docs":{"page":"api/quest-actions","id":"get-question","order":0}}},"/platform/quests/{questId}/complete":{"parameters":[{"name":"questId","in":"path","required":true,"description":"Quest ID.","schema":{"type":"integer"},"example":23}],"post":{"tags":["Platform delivery"],"operationId":"completePlatformQuest","summary":"Complete a quest","description":"Submits the participant’s evidence. AdQuest checks it on the server and, when it is valid, settles the reward.","security":[{"PlatformKey":[],"ParticipantToken":[]}],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"description":"Send as an HTTP header. A repeated key returns the earlier completion and never pays twice.","schema":{"type":"string"}},{"name":"X-AdQuest-Participant-Token","in":"header","required":true,"description":"The participant token minted by your backend (POST /platform/participant-sessions). Required: it is the participant's identity. A body external_user_id is optional and must match it (403 PARTICIPANT_MISMATCH); a missing or invalid token is 401 PARTICIPANT_AUTH_REQUIRED.","schema":{"type":"string"},"example":"PARTICIPANT_TOKEN"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"external_user_id":{"type":"string","maxLength":255,"description":"Your ID for the user. Max 255 characters.","example":"player-42"},"question_id":{"type":"integer","description":"Required for video and banner quests, and for surveys when answers is not sent.","example":5},"user_input":{"type":"string","description":"The option the participant chose. Required together with question_id.","example":"11"},"answers":{"type":"array","items":{},"description":"Survey only. One entry per answered question, each with its question_id."},"delivery_token":{"type":"string","description":"Token you received when the quest started."},"watch_duration":{"type":"number","description":"Seconds watched."}},"required":["external_user_id"]}}}},"responses":{"200":{"description":"The quest was completed. tokens_awarded is the participant’s share.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope"},"example":{"status":true,"code":"OK","message":"Quest completed successfully","data":{"status":true,"quest_id":23,"completed_at":"2026-01-01T12:00:00.000Z","tokens_awarded":10,"token_reward":10}}}}},"403":{"description":"PREVIEW_READONLY. A theme preview cannot complete quests."},"404":{"description":"QUEST_NOT_FOUND. The quest does not exist or is not available."},"422":{"description":"Validation failed, the evidence was rejected, or the quest is a streak quest. STREAK_CALLBACK_REQUIRED means the day must be submitted from the callback URL."}},"x-docs":{"page":"api/quest-actions","id":"complete-quest","order":7},"x-example-verified":true}},"/streak-callbacks/{token}":{"parameters":[{"name":"token","in":"path","required":true,"description":"The 64-character hex token from the quest’s callback URL.","schema":{"type":"string"},"example":"CALLBACK_TOKEN"}],"get":{"tags":["Platform delivery"],"operationId":"submitStreakCallbackGet","summary":"Record a streak day (GET)","description":"Same as the POST route, with user_id as a query parameter. Use POST when you can.","security":[],"parameters":[{"name":"user_id","in":"query","required":true,"description":"Your stable ID for the user. external_user_id is accepted as an alias.","schema":{"type":"string"},"example":"player-123"}],"responses":{"200":{"description":"The day was recorded. On the last day the quest completes and data also holds tokens_awarded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope"},"example":{"status":true,"code":"OK","message":"Streak day recorded","data":{"completed":false,"quest_id":7800,"external_user_id":"player-123","streak":{"current_day":2,"day_count":7,"status":"active","unlocked":false,"next_unlock_at":"2026-09-14T12:00:00+00:00","last_completed_day":1,"day_number":1,"micro_rewarded":false,"final_rewarded":false,"idempotent":false}}}}}},"404":{"description":"STREAK_CALLBACK_NOT_FOUND. The token does not match a quest."},"422":{"description":"INVALID_STREAK_USER or another streak error. data.reason says why."},"429":{"description":"RATE_LIMITED."}},"x-docs":{"page":"api/server-routes","id":"streak-callback-get","order":99}},"post":{"tags":["Platform delivery"],"operationId":"submitStreakCallback","summary":"Record a streak day from the creator callback URL","description":"Records one day of a streak quest. Call the callback URL from your backend after the user finishes that day’s action. GET works too, with user_id as a query parameter. A streak STEP of a multi-step quest has its own per-participant callback URL (callback_url in that step of the execution state); user_id is then optional and, if sent, must match the participant. The last day verifies only that step; the parent quest settles separately (data.step_verified, data.settlement_status).","security":[],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"description":"Optional key to make a retry safe.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"user_id":{"type":"string","description":"Your stable ID for the user. external_user_id, userId and externalUserId are accepted as aliases.","example":"player-123"}},"required":["user_id"]}}}},"responses":{"200":{"description":"The day was recorded. On the last day the quest completes and data also holds tokens_awarded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope"},"example":{"status":true,"code":"OK","message":"Streak day recorded","data":{"completed":false,"quest_id":7800,"external_user_id":"player-123","streak":{"current_day":2,"day_count":7,"status":"active","unlocked":false,"next_unlock_at":"2026-09-14T12:00:00+00:00","last_completed_day":1,"day_number":1,"micro_rewarded":false,"final_rewarded":false,"idempotent":false}}}}}},"404":{"description":"STREAK_CALLBACK_NOT_FOUND. The token does not match a quest."},"422":{"description":"INVALID_STREAK_USER or another streak error. data.reason says why."},"429":{"description":"RATE_LIMITED."}},"x-docs":{"page":"api/server-routes","id":"streak-callback","order":2},"x-example-verified":true}},"/platform/quests/{questId}/answer":{"parameters":[{"name":"questId","in":"path","required":true,"description":"Quest ID.","schema":{"type":"integer"},"example":23}],"post":{"tags":["Platform delivery"],"operationId":"answerPlatformQuest","summary":"Answer one question","description":"Answers the current question of the participant's attempt, in order and before its deadline. The last answer submits the attempt: it is accepted (202) and settled asynchronously; poll the completion status. Needs a reservation (the quest was started with reservations enabled). A request naming an earlier reservation_id is refused with 409 STALE_RESERVATION and changes nothing. A retry of the accepted final answer returns 202 (still processing) or the settled outcome, even after the attempt's deadline; it never creates a second submission.","security":[{"PlatformKey":[],"ParticipantToken":[]}],"parameters":[{"name":"X-AdQuest-Participant-Token","in":"header","required":true,"description":"The participant token minted by your backend (POST /platform/participant-sessions). Required: it is the participant's identity. A body external_user_id is optional and must match it (403 PARTICIPANT_MISMATCH); a missing or invalid token is 401 PARTICIPANT_AUTH_REQUIRED.","schema":{"type":"string"},"example":"PARTICIPANT_TOKEN"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"question_id":{"type":"integer","description":"The current question.","example":5},"option_id":{"type":"integer","description":"The chosen option (choice questions)."},"free_text":{"type":"string","description":"The answer (free-text questions)."},"reservation_id":{"type":"string","description":"The reservation_id returned by start (or the X-AdQuest-Reservation header). Required: an answer must name its attempt, and it must be the current one (409 STALE_RESERVATION otherwise, 422 RESERVATION_REQUIRED when missing)."}},"required":["question_id"]}}}},"responses":{"200":{"description":"The answer was recorded; the next question's deadline is in the data. correct is false when a video or banner quiz question was missed for the first time: the attempt goes on and the question is asked once more after the others (ask_order lists the question ids in the order they are asked). A second miss on it ends the attempt (ANSWER_REJECTED).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope"},"example":{"status":true,"code":"OK","message":"Answer recorded","data":{"state":"reserved","correct":true,"answered_count":1,"ask_order":[5,6],"question_due_at":"2026-11-07T10:00:22+00:00"}}}}},"202":{"description":"The final answer was accepted. The attempt is processing: capacity and budget stay reserved until settlement.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope"},"example":{"status":true,"code":"ACCEPTED","message":"Answer accepted","data":{"state":"processing","reservation_id":"0f4f6c52-3c7d-4d6e-9d0f-6f9d3f1d2a11","settlement_status":"pending","quest_id":23}}}}},"409":{"description":"RESERVATION_EXPIRED, WRONG_QUESTION, STALE_RESERVATION or TOO_EARLY."},"422":{"description":"The answer is not valid for the question."}},"x-docs":{"page":"api/quest-actions","id":"answer-question","order":6}}},"/platform/quests/{questId}/review":{"parameters":[{"name":"questId","in":"path","required":true,"description":"Quest ID.","schema":{"type":"integer"},"example":23}],"get":{"tags":["Platform delivery"],"operationId":"getPlatformQuestReview","summary":"Check a manual review","description":"Where the participant's manual_review submission stands: pending, approved, rejected or expired. Approval completes the quest and settles the reward without a second request; an approved review that is still settling retries here. A rejected or expired review may be resubmitted through the complete endpoint while can_resubmit is true. Returns status none before any submission.","security":[{"PlatformKey":[],"ParticipantToken":[]}],"parameters":[{"name":"X-AdQuest-Participant-Token","in":"header","required":true,"description":"The participant token minted by your backend (POST /platform/participant-sessions). Required: it is the participant's identity. A body external_user_id is optional and must match it (403 PARTICIPANT_MISMATCH); a missing or invalid token is 401 PARTICIPANT_AUTH_REQUIRED.","schema":{"type":"string"},"example":"PARTICIPANT_TOKEN"}],"responses":{"200":{"description":"The latest review for the participant. settlement_status is pending or settled once approved, otherwise null.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope"},"example":{"status":true,"code":"OK","message":"Custom review status","data":{"id":41,"status":"rejected","attempt":1,"evidence":"receipt-123","approved_at":null,"rejected_at":"2026-11-08T09:12:00+00:00","reject_reason":"Receipt is unreadable","expires_at":"2026-11-22T09:00:00+00:00","settled_at":null,"can_resubmit":true,"settlement_status":null}}}}}},"x-docs":{"page":"api/quest-actions","id":"get-review","order":9}}},"/platform/quests/{questId}/completion":{"parameters":[{"name":"questId","in":"path","required":true,"description":"Quest ID.","schema":{"type":"integer"},"example":23}],"get":{"tags":["Platform delivery"],"operationId":"getPlatformQuestCompletion","summary":"Check a completion","description":"Where the participant's attempt stands: reserved, processing, settled, rejected or expired, with the settlement status and the tokens awarded once settled.","security":[{"PlatformKey":[],"ParticipantToken":[]}],"parameters":[{"name":"X-AdQuest-Participant-Token","in":"header","required":true,"description":"The participant token minted by your backend (POST /platform/participant-sessions). Required: it is the participant's identity. A body external_user_id is optional and must match it (403 PARTICIPANT_MISMATCH); a missing or invalid token is 401 PARTICIPANT_AUTH_REQUIRED.","schema":{"type":"string"},"example":"PARTICIPANT_TOKEN"}],"responses":{"200":{"description":"The attempt's state. settlement_status is success, pending, failed or none.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope"},"example":{"status":true,"code":"OK","message":"Completion status","data":{"quest_id":23,"state":"settled","reservation_id":"0f4f6c52-3c7d-4d6e-9d0f-6f9d3f1d2a11","settlement_status":"success","tokens_awarded":7,"reason":null,"expires_at":null,"completed_at":"2026-11-07T10:00:41+00:00"}}}}}},"x-docs":{"page":"api/quest-actions","id":"get-completion","order":8}}},"/platform/balance":{"get":{"tags":["Wallet"],"operationId":"getParticipantBalance","summary":"Get the token balance","description":"Returns the participant’s current token balance.","security":[{"PlatformKey":[],"ParticipantToken":[]}],"responses":{"200":{"description":"The balance.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Envelope"},{"type":"object","properties":{"data":{"$ref":"#/components/schemas/TokenBalance"}}}]},"example":{"status":true,"code":"OK","message":"Participant token balance","data":{"tokens":120,"platform_user_id":55,"external_user_id":"player-42"}}}}},"401":{"description":"PARTICIPANT_AUTH_REQUIRED in data.code. The participant token is missing or invalid."}},"x-docs":{"page":"api/rewards","id":"get-balance","order":0}}},"/platform/quests/{questId}/metrics":{"parameters":[{"name":"questId","in":"path","required":true,"description":"Quest ID.","schema":{"type":"integer"},"example":23}],"post":{"tags":["Platform delivery"],"operationId":"recordProgressMetric","summary":"Record a progress metric","description":"Adds a measured amount to a quest that completes when a progress target is reached.","security":[{"PlatformKey":[],"ParticipantToken":[]}],"parameters":[{"name":"X-AdQuest-Participant-Token","in":"header","required":true,"description":"The participant token minted by your backend (POST /platform/participant-sessions). Required: it is the participant's identity. A body external_user_id is optional and must match it (403 PARTICIPANT_MISMATCH); a missing or invalid token is 401 PARTICIPANT_AUTH_REQUIRED.","schema":{"type":"string"},"example":"PARTICIPANT_TOKEN"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"external_user_id":{"type":"string","maxLength":255,"description":"Your ID for the user. Max 255 characters.","example":"player-42"},"event_id":{"type":"string","maxLength":128,"description":"Your unique ID for this measurement. Max 128 characters.","example":"evt_1001"},"amount":{"type":"number","description":"The amount to add.","example":5},"occurred_at":{"type":"string","description":"When it happened. Must fall inside the quest’s activity window.","example":"2026-01-01T12:00:00Z"}},"required":["external_user_id","event_id","amount","occurred_at"]}}}},"responses":{"200":{"description":"The metric was recorded. data holds the participant’s progress snapshot.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope"}}}},"403":{"description":"PREVIEW_READONLY. A theme preview cannot record progress."},"404":{"description":"QUEST_NOT_FOUND. The quest does not exist or is not available."},"422":{"description":"PROGRESS_UNSUPPORTED if the quest does not track progress, or another error code if the event was rejected."}},"x-docs":{"page":"api/quest-actions","id":"record-metric","order":2}}},"/platform/quests/{questId}/referrals":{"parameters":[{"name":"questId","in":"path","required":true,"description":"Quest ID.","schema":{"type":"integer"},"example":23}],"post":{"tags":["Platform delivery"],"operationId":"attributeReferral","summary":"Attribute a referral","description":"Credits the inviter when a new user arrives with their referral code. Referral quests only.","security":[{"PlatformKey":[],"ParticipantToken":[]}],"parameters":[{"name":"X-AdQuest-Participant-Token","in":"header","required":true,"description":"The participant token minted by your backend (POST /platform/participant-sessions). Required: it is the participant's identity. A body external_user_id is optional and must match it (403 PARTICIPANT_MISMATCH); a missing or invalid token is 401 PARTICIPANT_AUTH_REQUIRED.","schema":{"type":"string"},"example":"PARTICIPANT_TOKEN"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"external_user_id":{"type":"string","description":"The invited user. May also be sent as a query parameter.","example":"player-77"},"referral_code":{"type":"string","maxLength":32,"description":"The inviter’s code. Max 32 characters.","example":"ADA123"},"device_fingerprint":{"type":"string","maxLength":128,"description":"Max 128 characters."}},"required":["external_user_id","referral_code"]}}}},"responses":{"200":{"description":"Referral attributed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope"}}}},"401":{"description":"PARTICIPANT_AUTH_REQUIRED. external_user_id is missing."},"404":{"description":"QUEST_NOT_FOUND. The quest does not exist or is not available."},"422":{"description":"NOT_REFERRAL_QUEST if the quest is not a referral quest, or a referral error code in data.code."}},"x-docs":{"page":"api/quest-actions","id":"attribute-referral","order":5}}},"/platform/quests/{questId}/growth-status":{"parameters":[{"name":"questId","in":"path","required":true,"description":"Quest ID.","schema":{"type":"integer"},"example":23}],"get":{"tags":["Platform delivery"],"operationId":"readGrowthStatus","summary":"Read growth status","description":"Returns where the participant stands on a profile, referral, streak or custom quest.","security":[{"PlatformKey":[],"ParticipantToken":[]}],"parameters":[{"name":"external_user_id","in":"query","required":true,"description":"Your ID for the user. May also be sent in the body.","schema":{"type":"string"}},{"name":"X-AdQuest-Participant-Token","in":"header","required":true,"description":"The participant token minted by your backend (POST /platform/participant-sessions). Required: it is the participant's identity. A body external_user_id is optional and must match it (403 PARTICIPANT_MISMATCH); a missing or invalid token is 401 PARTICIPANT_AUTH_REQUIRED.","schema":{"type":"string"},"example":"PARTICIPANT_TOKEN"}],"responses":{"200":{"description":"data.type is the quest format. A profile quest adds data.profile with the completeness check. A referral quest adds data.referral with the participant’s code, invite link and attributed count. Streak and custom quests add their own state.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope"}}}},"401":{"description":"PARTICIPANT_AUTH_REQUIRED. external_user_id is missing."},"404":{"description":"QUEST_NOT_FOUND. The quest does not exist or is not available."}},"x-docs":{"page":"api/quest-actions","id":"growth-status","order":6}}},"/platform/quests/{questId}/stats":{"parameters":[{"name":"questId","in":"path","required":true,"description":"Quest ID.","schema":{"type":"integer"},"example":23}],"get":{"tags":["Platform delivery"],"operationId":"readQuestStats","summary":"Read quest stats","description":"Returns engagement counts for one quest on your platform.","security":[{"PlatformKey":[]}],"parameters":[{"name":"X-AdQuest-Participant-Token","in":"header","required":false,"description":"The participant token. Optional here. It adds per-user state such as completed.","schema":{"type":"string"},"example":"PARTICIPANT_TOKEN"}],"responses":{"200":{"description":"seen_count is participants who started the quest, clicked_count those who clicked, and answered_count those who completed it. engagement_rate is completed ÷ started as a percentage.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope"},"example":{"status":true,"code":"OK","message":"Quest stats","data":{"id":23,"name":"Watch our intro video","seen_count":120,"clicked_count":40,"answered_count":54,"engagement_rate":45}}}}},"404":{"description":"QUEST_NOT_FOUND. The quest does not exist or is not available."}},"x-docs":{"page":"api/quest-actions","id":"quest-stats","order":8}}},"/platform/balance/history":{"get":{"tags":["Platform delivery"],"operationId":"getBalanceHistory","summary":"Get the balance history","description":"Returns the participant’s token transactions, newest first, as a paginated list.","security":[{"PlatformKey":[],"ParticipantToken":[]}],"parameters":[{"name":"per_page","in":"query","required":false,"description":"Items per page. Defaults to 15.","schema":{"type":"integer"}},{"name":"page","in":"query","required":false,"description":"Page number.","schema":{"type":"integer"}}],"responses":{"200":{"description":"A paginated list. data holds the page of transactions with the pagination fields (current_page, last_page, per_page, total).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope"}}}},"401":{"description":"PARTICIPANT_AUTH_REQUIRED in data.code. The participant token is missing or invalid."}},"x-docs":{"page":"api/rewards","id":"balance-history","order":1}}},"/platform/lotteries":{"get":{"tags":["Platform delivery"],"operationId":"listLotteries","summary":"List lotteries","description":"Returns the lotteries visible to this platform and participant country, with active ones first.","security":[{"PlatformKey":[],"ParticipantToken":[]}],"parameters":[{"name":"filter","in":"query","required":false,"description":"active (default), upcoming, completed or all.","schema":{"type":"string"}},{"name":"per_page","in":"query","required":false,"description":"Items per page. Defaults to 20.","schema":{"type":"integer"}}],"responses":{"200":{"description":"A page of lotteries.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/PlainEnvelope"},{"type":"object","properties":{"data":{"type":"object","properties":{"lotteries":{"type":"array","items":{"$ref":"#/components/schemas/Lottery"}},"pagination":{"$ref":"#/components/schemas/Pagination"}}}}}]},"example":{"status":true,"message":"Lotteries list","data":{"lotteries":[{"id":4,"name":"Weekly draw","description":"Win a gift card.","locale":"en","requested_locale":"en","source_locale":"en","is_fallback":false,"availability":{"policy_type":"global","policy_version":1,"eligibility":"eligible","warning":false,"countries":[]},"visibility":"public","entry_cost_tokens":20,"max_entries_per_participant":5,"starts_at":"2026-01-01T00:00:00+00:00","ends_at":"2026-01-08T00:00:00+00:00","lifecycle_status":"active","media_url":null,"prize_count":3,"participants_count":128,"hottest_position":1,"my_entry_count":2,"winners_count":null}],"pagination":{"current_page":1,"last_page":1,"per_page":20,"total":1}}}}}},"401":{"description":"PARTICIPANT_AUTH_REQUIRED in data.code. The participant token is missing or invalid."}},"x-docs":{"page":"api/rewards","id":"list-lotteries","order":2},"x-example-verified":true}},"/platform/lotteries/{lotteryId}":{"parameters":[{"name":"lotteryId","in":"path","required":true,"description":"Lottery ID.","schema":{"type":"integer"},"example":4}],"get":{"tags":["Platform delivery"],"operationId":"getLottery","summary":"Get a lottery","description":"Returns one lottery with its prizes and the participant’s own entries.","security":[{"PlatformKey":[],"ParticipantToken":[]}],"responses":{"200":{"description":"The lottery with its prizes and the participant’s own entry count.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/PlainEnvelope"},{"type":"object","properties":{"data":{"type":"object","properties":{"lottery":{"allOf":[{"$ref":"#/components/schemas/Lottery"},{"type":"object","properties":{"awards":{"type":"array","items":{"type":"object"}},"my_entry_count":{"type":"integer"},"remaining_entries":{"type":"integer"}}}]}}}}}]},"example":{"status":true,"message":"Lottery details","data":{"lottery":{"id":4,"name":"Weekly draw","description":"Win a gift card.","locale":"en","requested_locale":"en","source_locale":"en","is_fallback":false,"availability":{"policy_type":"global","policy_version":1,"eligibility":"eligible","warning":false,"countries":[]},"visibility":"public","entry_cost_tokens":20,"max_entries_per_participant":5,"starts_at":"2026-01-01T00:00:00+00:00","ends_at":"2026-01-08T00:00:00+00:00","lifecycle_status":"active","media_url":null,"prize_count":3,"participants_count":128,"hottest_position":1,"awards":[{"id":1,"name":"Gift card","description":null,"locale":"en","requested_locale":"en","source_locale":"en","is_fallback":false,"type":"digital","count":3,"priority":1}],"my_entry_count":0,"remaining_entries":5}}}}}},"404":{"description":"LOTTERY_NOT_FOUND in data.code. The lottery does not exist or is not visible to this platform.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlainEnvelope"},"example":{"status":false,"message":"Lottery not found","data":{"code":"LOTTERY_NOT_FOUND"}}}}}},"x-docs":{"page":"api/rewards","id":"get-lottery","order":3},"x-example-verified":true}},"/platform/lotteries/{lotteryId}/enter":{"parameters":[{"name":"lotteryId","in":"path","required":true,"description":"Lottery ID.","schema":{"type":"integer"},"example":4}],"post":{"tags":["Platform delivery"],"operationId":"enterLottery","summary":"Enter a lottery","description":"Spends tokens to buy entries. Send a unique idempotency_key for each purchase and reuse it when you retry after an uncertain response, so the participant is never charged twice.","security":[{"PlatformKey":[],"ParticipantToken":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"entry_count":{"type":"integer","description":"How many entries to buy, 1 to 100.","example":2},"idempotency_key":{"type":"string","maxLength":191,"description":"Unique key for this purchase attempt. Max 191 characters.","example":"enter-4-player-42-001"}},"required":["entry_count","idempotency_key"]}}}},"responses":{"200":{"description":"The entries were purchased.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlainEnvelope"},"example":{"status":true,"message":"Lottery entries purchased","data":{"purchase":{"id":301,"lottery_id":4,"entry_count":2,"unit_cost_tokens":20,"total_cost_tokens":40,"status":"completed","ticket_numbers":["1G2736","K7Q3X9"]},"eligibility_snapshot":{"country_code":"IR","country_source":"platform","eligibility_status":"eligible","availability_policy_version":1}}}}}},"401":{"description":"PARTICIPANT_AUTH_REQUIRED in data.code. The participant token is missing or invalid."},"403":{"description":"PREVIEW_READONLY. A theme preview cannot enter lotteries."},"404":{"description":"LOTTERY_NOT_FOUND in data.code. The lottery does not exist or is not visible to this platform.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlainEnvelope"},"example":{"status":false,"message":"Lottery not found","data":{"code":"LOTTERY_NOT_FOUND"}}}}},"409":{"description":"DUPLICATE_PURCHASE. The idempotency_key was already used for a different lottery or a different participant. Use a new key for each purchase attempt.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlainEnvelope"},"example":{"status":false,"message":"Duplicate purchase request","data":{"code":"DUPLICATE_PURCHASE"}}}}},"422":{"description":"Validation failed, or the purchase was refused. data.code says why, for example INSUFFICIENT_TOKENS, LOTTERY_CLOSED or ENTRY_LIMIT_REACHED."}},"x-docs":{"page":"api/rewards","id":"enter-lottery","order":4},"x-example-verified":true}},"/platform/lotteries/{lotteryId}/entries":{"parameters":[{"name":"lotteryId","in":"path","required":true,"description":"Lottery ID.","schema":{"type":"integer"},"example":4}],"get":{"tags":["Platform delivery"],"operationId":"listEntries","summary":"List my entries","description":"Returns the participant’s entries and purchases for one lottery.","security":[{"PlatformKey":[],"ParticipantToken":[]}],"responses":{"200":{"description":"data has entries, purchases and entry_count.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlainEnvelope"}}}},"401":{"description":"PARTICIPANT_AUTH_REQUIRED in data.code. The participant token is missing or invalid."},"404":{"description":"LOTTERY_NOT_FOUND in data.code. The lottery does not exist or is not visible to this platform.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlainEnvelope"},"example":{"status":false,"message":"Lottery not found","data":{"code":"LOTTERY_NOT_FOUND"}}}}}},"x-docs":{"page":"api/rewards","id":"my-entries","order":5}}},"/platform/lotteries/{lotteryId}/results":{"parameters":[{"name":"lotteryId","in":"path","required":true,"description":"Lottery ID.","schema":{"type":"integer"},"example":4}],"get":{"tags":["Platform delivery"],"operationId":"getLotteryResults","summary":"Get lottery results","description":"Returns the winners once the lottery is completed. Before that, winners is an empty list.","security":[{"PlatformKey":[],"ParticipantToken":[]}],"responses":{"200":{"description":"data has lifecycle_status and winners. Winners are returned only when lifecycle_status is completed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlainEnvelope"},"example":{"status":true,"message":"Lottery results unavailable","data":{"lifecycle_status":"active","winners":[]}}}}},"404":{"description":"LOTTERY_NOT_FOUND in data.code. The lottery does not exist or is not visible to this platform.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlainEnvelope"},"example":{"status":false,"message":"Lottery not found","data":{"code":"LOTTERY_NOT_FOUND"}}}}}},"x-docs":{"page":"api/rewards","id":"lottery-results","order":6}}},"/platform/lottery-tickets":{"get":{"tags":["Platform delivery"],"operationId":"listMyTickets","summary":"List my tickets","description":"Returns every ticket code the participant holds, one group per draw, the draw they entered most recently first.","security":[{"PlatformKey":[],"ParticipantToken":[]}],"responses":{"200":{"description":"The participant’s ticket codes grouped by draw.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/PlainEnvelope"},{"type":"object","properties":{"data":{"type":"object","properties":{"lotteries":{"type":"array","items":{"type":"object","properties":{"lottery_id":{"type":"integer"},"name":{"type":"string"},"ends_at":{"type":["string","null"],"format":"date-time"},"lifecycle_status":{"type":"string"},"tickets":{"type":"array","items":{"type":"object","properties":{"ticket_number":{"type":"string","example":"1G2736"},"has_won":{"type":"boolean"}}}}}}}}}}}]},"example":{"status":true,"message":"My lottery tickets","data":{"lotteries":[{"lottery_id":4,"name":"Weekly draw","ends_at":"2026-02-01T00:00:00+00:00","lifecycle_status":"active","tickets":[{"ticket_number":"1G2736","has_won":false},{"ticket_number":"K7Q3X9","has_won":false}]}]}}}}},"401":{"description":"PARTICIPANT_AUTH_REQUIRED in data.code. The participant token is missing or invalid."}},"x-docs":{"page":"api/rewards","id":"my-tickets","order":7}}},"/platform/lottery-claims":{"get":{"tags":["Platform delivery"],"operationId":"listClaims","summary":"List my claims","description":"Returns the prizes the participant has won, newest first.","security":[{"PlatformKey":[],"ParticipantToken":[]}],"responses":{"200":{"description":"The participant’s claims. Each claim also has an eligibility_snapshot.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/PlainEnvelope"},{"type":"object","properties":{"data":{"type":"object","properties":{"claims":{"type":"array","items":{"$ref":"#/components/schemas/LotteryClaim"}}}}}}]},"example":{"status":true,"message":"My lottery claims","data":{"claims":[{"id":91,"lottery_id":4,"lottery_name":"Weekly draw","status":"pending","award_name":"Gift card","award_type":"gift","submitted_at":null,"fulfilled_at":null,"claim_expires_at":"2026-02-01T00:00:00+00:00"}]}}}}},"401":{"description":"PARTICIPANT_AUTH_REQUIRED in data.code. The participant token is missing or invalid."}},"x-docs":{"page":"api/rewards","id":"my-claims","order":8}}},"/platform/lottery-claims/{claimId}":{"parameters":[{"name":"claimId","in":"path","required":true,"description":"Claim ID from the claims list.","schema":{"type":"integer"},"example":91}],"post":{"tags":["Platform delivery"],"operationId":"submitClaim","summary":"Submit a claim","description":"Submits the winner’s claim details. The fields the payload needs depend on the prize and the claim form.","security":[{"PlatformKey":[],"ParticipantToken":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"payload":{"type":"object","description":"The claim details the prize requires.","example":{"email":"ada@example.com","name":"Ada"}}},"required":["payload"]}}}},"responses":{"200":{"description":"The claim was submitted. data.claim is the updated claim.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlainEnvelope"}}}},"401":{"description":"PARTICIPANT_AUTH_REQUIRED in data.code. The participant token is missing or invalid."},"403":{"description":"PREVIEW_READONLY. A theme preview cannot submit claims."},"404":{"description":"CLAIM_NOT_AVAILABLE. The claim does not belong to this participant."},"422":{"description":"Validation failed, or the claim is closed or expired. data.code says why."}},"x-docs":{"page":"api/rewards","id":"submit-claim","order":9}}},"/platform/config":{"get":{"tags":["Platform delivery"],"operationId":"getPlatformConfig","summary":"Get platform config","description":"Returns the published branding and configuration for the platform that owns the API key.","security":[{"PlatformKey":[]}],"responses":{"200":{"description":"The platform’s name and published theme.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope"},"example":{"status":true,"code":"OK","message":"Platform config","data":{"name":"Subway Sample","theme":{"colors":{"primary":"#FB6514"}},"public_id":"63e1528f-0214-439e-b233-a9b99c54fb77"}}}}}},"x-docs":{"page":"api/platform","id":"platform-config","order":0}}},"/platform/public-config/{publicId}":{"parameters":[{"name":"publicId","in":"path","required":true,"description":"The platform’s UUID, from your Embed URL.","schema":{"type":"string"},"example":"63e1528f-0214-439e-b233-a9b99c54fb77"}],"get":{"tags":["Platform delivery"],"operationId":"getPublicConfig","summary":"Get public config","description":"Returns the same configuration by the platform’s public ID. The hosted embed uses it to load your branding. No credentials are needed.","security":[],"responses":{"200":{"description":"The platform’s name and published theme. The response is never cached.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlainEnvelope"}}}},"404":{"description":"The ID is not a UUID, or no accepted and active platform has it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlainEnvelope"},"example":{"status":false,"message":"Platform not found","data":null}}}}},"x-docs":{"page":"api/platform","id":"public-config","order":1}}},"/platform/lottery-claims/{claimId}/contact":{"parameters":[{"name":"claimId","in":"path","required":true,"description":"Claim ID from the lottery.winner webhook.","schema":{"type":"integer"},"example":91}],"post":{"tags":["Platform delivery"],"operationId":"postWinnerContact","summary":"Post a winner’s contact","description":"Sends AdQuest the winner’s email after a lottery.winner webhook, so AdQuest can notify them. Call it only for the winner of that claim.","security":[{"PlatformSecret":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"external_user_id":{"type":"string","maxLength":255,"description":"Your ID for the winner. Max 255 characters.","example":"player-42"},"email":{"type":"string","maxLength":255,"description":"The winner’s email. Max 255 characters.","example":"ada@example.com"},"display_name":{"type":"string","maxLength":255,"description":"The winner’s name. Max 255 characters.","example":"Ada"}},"required":["external_user_id","email","display_name"]}}}},"responses":{"200":{"description":"The contact was received.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope"},"example":{"status":true,"code":"OK","message":"Winner contact received","data":{"claim_id":91,"notified":true}}}}},"401":{"description":"The Secret Key is missing or wrong."},"403":{"description":"The platform is not accepted and active."},"404":{"description":"CLAIM_NOT_AVAILABLE."},"429":{"description":"RATE_LIMITED. Too many contact requests."}},"x-docs":{"page":"api/server-routes","id":"winner-contact","order":0}}},"/platform/quests/{questId}/execution":{"parameters":[{"name":"questId","in":"path","required":true,"description":"Quest ID.","schema":{"type":"integer"},"example":23}],"get":{"tags":["Platform delivery"],"operationId":"getPlatformQuestExecution","summary":"Read a multi-step execution","description":"State of the participant's multi-step execution: the definition version it is bound to, every step with its status (pending, in_review, verified, rejected) and availability, whether all required steps are verified, and the settlement status of the parent quest. Returns 409 NOT_STARTED before start. Disabled (404 MULTI_STEP_DISABLED) unless the multi-step feature flag is on.","security":[{"PlatformKey":[],"ParticipantToken":[]}],"parameters":[{"name":"X-AdQuest-Participant-Token","in":"header","required":true,"description":"The participant token minted by your backend (POST /platform/participant-sessions). Required: it is the participant's identity. A body external_user_id is optional and must match it (403 PARTICIPANT_MISMATCH); a missing or invalid token is 401 PARTICIPANT_AUTH_REQUIRED.","schema":{"type":"string"},"example":"PARTICIPANT_TOKEN"}],"responses":{"200":{"description":"The execution state.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope"},"example":{"status":true,"code":"OK","message":"Execution state","data":{"quest_id":23,"execution_id":7,"steps_version":1,"ordering":"sequential","state":"reserved","steps":[{"step_key":"buy","position":1,"format":"custom","required":true,"status":"verified","attempts":1,"available":true}],"steps_complete":false,"settlement_status":"none","unsettled_reason":null,"tokens_awarded":null}}}}}},"x-docs":{"page":"api/quest-actions","id":"get-execution","order":10}}},"/platform/quests/{questId}/steps/{key}":{"parameters":[{"name":"questId","in":"path","required":true,"description":"Quest ID.","schema":{"type":"integer"},"example":23},{"name":"key","in":"path","required":true,"description":"Step key.","schema":{"type":"string"},"example":"buy"}],"get":{"tags":["Platform delivery"],"operationId":"getPlatformQuestStep","summary":"Read one step","description":"Status of one step of the execution, including the latest manual review (never expected answers or callback URLs). When the owner or the platform's activity definition set a host deep link, config carries action_url (https or an app scheme) and an optional action_label; the SDK only navigates to it and the host app still proves the step.","security":[{"PlatformKey":[],"ParticipantToken":[]}],"parameters":[{"name":"X-AdQuest-Participant-Token","in":"header","required":true,"description":"The participant token minted by your backend (POST /platform/participant-sessions). Required: it is the participant's identity. A body external_user_id is optional and must match it (403 PARTICIPANT_MISMATCH); a missing or invalid token is 401 PARTICIPANT_AUTH_REQUIRED.","schema":{"type":"string"},"example":"PARTICIPANT_TOKEN"}],"responses":{"200":{"description":"The execution state.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope"},"example":{"status":true,"code":"OK","message":"Execution state","data":{"quest_id":23,"execution_id":7,"steps_version":1,"ordering":"sequential","state":"reserved","steps":[{"step_key":"buy","position":1,"format":"custom","required":true,"status":"verified","attempts":1,"available":true}],"steps_complete":false,"settlement_status":"none","unsettled_reason":null,"tokens_awarded":null}}}}}},"x-docs":{"page":"api/quest-actions","id":"get-step","order":11}}},"/platform/quests/{questId}/steps/{key}/evidence":{"parameters":[{"name":"questId","in":"path","required":true,"description":"Quest ID.","schema":{"type":"integer"},"example":23},{"name":"key","in":"path","required":true,"description":"Step key.","schema":{"type":"string"},"example":"buy"}],"post":{"tags":["Platform delivery"],"operationId":"submitPlatformQuestStepEvidence","summary":"Submit step evidence","description":"Ask the server to verify a step. The server decides: a body of completed:true is ignored. action=start starts a timed (video/banner) step, action=click records a visit click, evidence/answers/evidence_type carry survey answers or manual-review proof. Server-attested events are sent to POST /events with step_key and a v2 signature (v2.eventId.eventName.questId.participantId.timestamp.stepKey.stepsVersion.executionId, HMAC-SHA256). When the last required step is verified the parent is admitted for settlement without another request; poll the execution or completion for the result.","security":[{"PlatformKey":[],"ParticipantToken":[]}],"parameters":[{"name":"X-AdQuest-Participant-Token","in":"header","required":true,"description":"The participant token minted by your backend (POST /platform/participant-sessions). Required: it is the participant's identity. A body external_user_id is optional and must match it (403 PARTICIPANT_MISMATCH); a missing or invalid token is 401 PARTICIPANT_AUTH_REQUIRED.","schema":{"type":"string"},"example":"PARTICIPANT_TOKEN"}],"responses":{"200":{"description":"The execution state.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope"},"example":{"status":true,"code":"OK","message":"Execution state","data":{"quest_id":23,"execution_id":7,"steps_version":1,"ordering":"sequential","state":"reserved","steps":[{"step_key":"buy","position":1,"format":"custom","required":true,"status":"verified","attempts":1,"available":true}],"steps_complete":false,"settlement_status":"none","unsettled_reason":null,"tokens_awarded":null}}}}},"202":{"description":"Evidence accepted for asynchronous review.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope"}}}},"409":{"description":"STEP_OUT_OF_ORDER, STEP_NOT_STARTED, RESERVATION_EXPIRED, NOT_STARTED or QUEST_PROCESSING.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"The server could not verify the step from the evidence (CLICK_REQUIRED, TIMER_NOT_FINISHED, CUSTOM_EVIDENCE_MISSING, ...).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-docs":{"page":"api/quest-actions","id":"submit-step-evidence","order":12},"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["start","click"],"description":"start begins a timed (video/banner) step; click records the click of a visit step. Omit to submit evidence."},"evidence":{"type":"string","maxLength":2000,"description":"Proof text for a manual_review step (max 2000 characters)."},"evidence_type":{"type":"string","description":"Evidence type the step expects (must match the step configuration)."},"answers":{"type":"array","items":{"type":"object"},"description":"Survey answers: [{question_id, user_input}]."}}}}}}}}},"components":{"securitySchemes":{"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"},"PlatformKey":{"type":"apiKey","in":"header","name":"X-AdQuest-Platform-Key","description":"Publisher platform API key (aqk_...). Accepts active keys and previous keys within the rotation grace window."},"PlatformSecret":{"type":"apiKey","in":"header","name":"X-AdQuest-Platform-Secret","description":"Secret Key (aqs_) for server-to-server calls such as creating participant sessions. Keep it on your server. Distinct from PlatformKey."},"ParticipantToken":{"type":"apiKey","in":"header","name":"X-AdQuest-Participant-Token","description":"Participant token (a JWT) from a participant session. It expires after 15 minutes. Refresh it through your backend."}},"schemas":{"Envelope":{"type":"object","required":["status","code","message","data"],"properties":{"status":{"type":"boolean"},"code":{"type":"string"},"message":{"type":"string"},"data":{},"errors":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}}}},"Error":{"type":"object","required":["status","code","message"],"properties":{"status":{"const":false},"code":{"type":"string"},"message":{"type":"string"},"data":{},"errors":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}}}},"PlainEnvelope":{"type":"object","description":"Envelope used by lottery, claim and public-config routes. It has no top-level code. Read data.code on errors.","required":["status","message","data"],"properties":{"status":{"type":"boolean"},"message":{"type":"string"},"data":{}}},"Pagination":{"type":"object","properties":{"current_page":{"type":"integer"},"last_page":{"type":"integer"},"per_page":{"type":"integer"},"total":{"type":"integer"}}},"ParticipantSession":{"type":"object","required":["participant_token","expires_in"],"properties":{"participant_token":{"type":"string","description":"A signed JWT. Send it as X-AdQuest-Participant-Token."},"expires_in":{"type":"integer","description":"Seconds until the token expires. Always 900 (15 minutes).","example":900},"expires_at":{"type":"string","format":"date-time"},"locale_resolution":{"type":"object","properties":{"requested":{"type":"string"},"effective":{"type":"string"},"source":{"type":"string"}}},"platform_user":{"type":"object","properties":{"id":{"type":"integer"},"external_user_id":{"type":"string"},"display_name":{"type":["string","null"]},"avatar_url":{"type":["string","null"]},"country_code":{"type":["string","null"]},"country_name":{"type":["string","null"]},"eligibility_country_code":{"type":["string","null"]},"eligibility_country_source":{"type":["string","null"]}}},"eligibility":{"type":"object","description":"What the participant has shared, used by profile quests."}}},"TokenBalance":{"type":"object","properties":{"tokens":{"type":"integer"},"platform_user_id":{"type":"integer"},"external_user_id":{"type":"string"}}},"Quest":{"type":"object","description":"A quest as returned to participants. Format-specific rule fields are filled only for that format.","properties":{"id":{"type":"integer"},"name":{"type":"string"},"type":{"type":"string","description":"Format key: video, banner, survey, visit, event, profile, referral, streak or custom."},"type_name":{"type":"string"},"visibility":{"type":"string"},"platform_id":{"type":["integer","null"]},"token_reward":{"type":["integer","null"],"description":"Tokens this participant earns."},"token_reward_total":{"type":["integer","null"],"description":"Total reward before the participant and AdQuest split it."},"gift_reward":{"type":["object","null"]},"gift_reward_expires_at":{"type":["string","null"]},"status":{"type":"string"},"screen":{"type":"string","description":"The widget screen that renders this quest."},"content":{"type":["string","null"]},"cover":{"type":["string","null"]},"redirect_url":{"type":["string","null"]},"display_duration":{"type":["integer","null"],"description":"Seconds a banner must be shown. Banner quests only."},"start_date":{"type":["string","null"]},"end_date":{"type":["string","null"]},"completed":{"type":"boolean","description":"Whether this participant already completed the quest. Needs external_user_id."},"completion_condition":{"type":"object","description":"What the participant must do. Flags: requires_timer, requires_question, requires_click, requires_event, requires_profile, requires_referral, requires_streak, requires_custom, and answer_mode."},"event_rule":{"type":["object","null"]},"profile_rule":{"type":["object","null"]},"referral_rule":{"type":["object","null"]},"streak_rule":{"type":["object","null"]},"custom_rule":{"type":["object","null"]},"media":{"type":"object","description":"The quest media: type, url and cover."},"proof":{"type":"object","description":"The evidence this format needs (type, version, media, timed). Present only when the runtime supports the format."},"reward":{"type":"object"},"reward_unit":{"type":["object","null"],"description":"The platform's own reward unit when the owner fulfils the reward; null means AdQuest tokens.","properties":{"code":{"type":"string"},"label":{"type":"string"},"icon_url":{"type":["string","null"]}}},"reward_is_max":{"type":"boolean","description":"True when token_reward is the most a participant can earn (per-second, per-question and similar metrics), not a fixed amount."},"availability":{"type":"string","enum":["locked"],"description":"Present only when the participant cannot start this quest: 'locked' with a lock_reason. Needs external_user_id."},"lock_reason":{"type":"string","enum":["ATTEMPTS_EXHAUSTED"],"description":"Why the quest is locked. ATTEMPTS_EXHAUSTED: every allowed attempt was used without completing it; a start is refused with ATTEMPTS_EXHAUSTED."}}},"Lottery":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"},"description":{"type":["string","null"]},"locale":{"type":"string"},"requested_locale":{"type":"string"},"source_locale":{"type":"string"},"is_fallback":{"type":"boolean"},"availability":{"type":"object","description":"Whether this participant may see and enter the lottery, by country policy.","properties":{"policy_type":{"type":"string"},"policy_version":{"type":"integer"},"eligibility":{"type":"string"},"warning":{"type":"boolean"},"countries":{"type":"array","items":{"type":"string"}}}},"visibility":{"type":"string"},"entry_cost_tokens":{"type":"integer"},"max_entries_per_participant":{"type":"integer"},"starts_at":{"type":["string","null"]},"ends_at":{"type":["string","null"]},"lifecycle_status":{"type":"string"},"media_url":{"type":["string","null"]},"prize_count":{"type":"integer"},"participants_count":{"type":"integer"},"hottest_position":{"type":["integer","null"],"description":"The platform marked this draw hottest: it leads the rewards tab as a hero card, in this order (1 first). Null when not marked. Marked open draws come first in the list."},"my_entry_count":{"type":"integer","description":"Tickets the calling participant holds in this lottery (0 without a participant). Detail and list."},"winners_count":{"type":["integer","null"],"description":"Winners of a completed lottery; null while it has not finished. List only."}}},"LotteryClaim":{"type":"object","properties":{"id":{"type":"integer"},"lottery_id":{"type":"integer"},"lottery_name":{"type":["string","null"]},"status":{"type":"string"},"award_name":{"type":["string","null"]},"award_type":{"type":["string","null"]},"submitted_at":{"type":["string","null"]},"fulfilled_at":{"type":["string","null"]},"claim_expires_at":{"type":["string","null"]},"eligibility_snapshot":{"type":["object","null"]}}}}}}