{"record":{"id":"ba50e749d4dc5496","repo":"yikart/AiToEarn","slug":"channelwebhookinvalidverifytoken-ba50e7","errorCode":null,"errorMessage":"ChannelWebhookInvalidVerifyToken","messagePattern":"ChannelWebhookInvalidVerifyToken","errorType":"http","errorClass":null,"httpStatus":403,"severity":"error","filePath":"project/aitoearn-backend/apps/aitoearn-server/src/core/channels/platforms/instagram/instagram-webhook.provider.ts","lineNumber":75,"sourceCode":"    }\n    response.status(200).send('EVENT_RECEIVED')\n  }\n\n  private handleChallenge(request: Request, response: Response): void {\n    const {\n      'hub.mode': mode,\n      'hub.verify_token': verifyToken,\n      'hub.challenge': challenge,\n    } = request.query as InstagramWebhookChallengeQuery\n    if (\n      mode === 'subscribe'\n      && verifyToken === this.config.webhookVerifyToken\n      && challenge\n    ) {\n      response.status(200).send(challenge)\n      return\n    }\n    response.status(403).send(getCodeMessage(ResponseCode.ChannelWebhookInvalidVerifyToken, undefined, getLocale()))\n  }\n\n  private verify(request: RawBodyRequest): boolean {\n    const rawBody = request.rawBody\n    const signature = this.getHeader(request, 'x-hub-signature-256')\n    if (!signature?.startsWith('sha256=') || !rawBody || !this.config.clientSecret) {\n      return false\n    }\n\n    const expected = `sha256=${createHmac('sha256', this.config.clientSecret).update(rawBody).digest('hex')}`\n    return this.safeEqual(signature, expected)\n  }\n\n  private parseMetaBody(request: Request): InstagramWebhookBody | null {\n    const result = InstagramWebhookBodySchema.safeParse(request.body)\n    if (!result.success) {\n      this.logger.warn(\n        { platform: AccountType.Instagram, issues: result.error.issues },","sourceCodeStart":57,"sourceCodeEnd":93,"githubUrl":"https://github.com/yikart/AiToEarn/blob/d3aa8bea5b146a8675607cf0144d891aad3e9683/project/aitoearn-backend/apps/aitoearn-server/src/core/channels/platforms/instagram/instagram-webhook.provider.ts#L57-L93","documentation":"During Meta's GET subscription handshake (hub.mode=subscribe), the provider echoes hub.challenge only when hub.verify_token equals config.webhookVerifyToken. On mismatch it responds 403 with ChannelWebhookInvalidVerifyToken, telling Meta the endpoint could not be validated.","triggerScenarios":"GET webhook verification request where query.hub.verify_token is absent or does not equal this.config.webhookVerifyToken, or hub.challenge is missing.","commonSituations":"Verify token typed incorrectly in Meta App Dashboard vs env var; token rotated in one place only; subscribing the wrong callback URL; whitespace/quoting issues in the env value.","solutions":["Set the exact same verify token in Meta App Dashboard and in the app's webhookVerifyToken config","Redeploy/restart after changing the token env var","Confirm the callback URL registered in Meta points at this endpoint","Trim quotes/whitespace from the token env value"],"exampleFix":"// before\nWEBHOOK_VERIFY_TOKEN=\"my token \"   // trailing space\n// after\nWEBHOOK_VERIFY_TOKEN=my-precise-token  // matches dashboard value exactly","handlingStrategy":"validation","validationCode":"const mode = query['hub.mode']; const token = query['hub.verify_token']; const challenge = query['hub.challenge']\nif (mode !== 'subscribe' || typeof token !== 'string' || token !== process.env.INSTAGRAM_VERIFY_TOKEN || !challenge) {\n  throw new Error('verify token mismatch')\n}","typeGuard":"function isValidChallenge(q: unknown): q is { 'hub.mode': string; 'hub.verify_token': string; 'hub.challenge': string } {\n  const o = q as Record<string, unknown>\n  return typeof o['hub.verify_token'] === 'string' && typeof o['hub.challenge'] === 'string'\n}","tryCatchPattern":null,"preventionTips":["Use the identical verify token string in Meta dashboard and env config","Avoid whitespace/quoting drift in env values","Test the handshake after every token change"],"tags":["webhook","verify-token","instagram","meta"],"backgroundTag":"webhook-verify-token-mismatch","analyzedSha":"d3aa8bea5b146a8675607cf0144d891aad3e9683","analyzedAt":"2026-08-31T14:19:24.185Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}