Cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Webhook subscriptions | Create Subscriber returns 403 | "The caller does not have permission"

Even though all the steps are followed and all the permission are given, I'm still getting 403.

{
  "error": {
    "code": 403,
    "message": "The caller does not have permission",
    "errors": [
      {
        "message": "The caller does not have permission",
        "domain": "global",
        "reason": "forbidden"
      }
    ],
    "status": "PERMISSION_DENIED"
  }
}

Attaching node js script which I used for calling.

/**
 * Calls Google Health API CreateSubscriber for the webhook URL.
 *
 * Prerequisites:
 * - Application Default Credentials with permission health.subscribers.create on the project
 *   (often: gcloud auth application-default login as a user with that role, or a service account key).
 * - Public HTTPS endpoint that already implements POST /trackers/fitbit/webhook and passes verification.
 *
 * Env:
 * - GOOGLE_APPLICATION_CREDENTIALS (required) absolute path to the service account JSON key file
 * - GOOGLE_CLOUD_PROJECT_ID (required)
 * - GOOGLE_HEALTH_WEBHOOK_ENDPOINT_URI (required) e.g. https://api.example.com/trackers/fitbit/webhook
 * - GOOGLE_HEALTH_WEBHOOK_AUTHORIZATION_TOKEN (required) same full Authorization value as the server (e.g. Bearer ...)
 * - GOOGLE_HEALTH_SUBSCRIBER_ID (optional) default "trackers-webhook"
 */

const fs = require("fs");
const path = require("path");
require("dotenv").config({
  path: path.resolve(__dirname, "../environment/env/.env.sandbox"),
});

const { GoogleAuth } = require("google-auth-library");

async function main() {
  const keyPath = (process.env.GOOGLE_APPLICATION_CREDENTIALS || "").trim();
  if (!keyPath) {
    console.error(
      "Missing GOOGLE_APPLICATION_CREDENTIALS (path to your service account JSON key).\n" +
        "Example:\n" +
        '  export GOOGLE_APPLICATION_CREDENTIALS="/Users/you/Downloads/your-project-xxxxx.json"\n' +
        "Or add that variable to environment/env/.env.sandbox (never commit the key file itself).\n" +
        "This is separate from gcloud; you only need the key downloaded from IAM → Service accounts → Keys."
    );
    process.exit(1);
  }
  if (!fs.existsSync(keyPath)) {
    console.error(`GOOGLE_APPLICATION_CREDENTIALS file not found:\n  ${keyPath}`);
    process.exit(1);
  }

  const projectId = process.env.GOOGLE_CLOUD_PROJECT_ID;
  const endpointUri = process.env.GOOGLE_HEALTH_WEBHOOK_ENDPOINT_URI;
  const secret = process.env.GOOGLE_HEALTH_WEBHOOK_AUTHORIZATION_TOKEN;
  const subscriberId = process.env.GOOGLE_HEALTH_SUBSCRIBER_ID || "trackers-webhook";

  if (!projectId || !endpointUri || !secret) {
    console.error(
      "Set GOOGLE_CLOUD_PROJECT_ID, GOOGLE_HEALTH_WEBHOOK_ENDPOINT_URI, and GOOGLE_HEALTH_WEBHOOK_AUTHORIZATION_TOKEN"
    );
    process.exit(1);
  }

  const auth = new GoogleAuth({
    keyFilename: keyPath,
    scopes: ["https://www.googleapis.com/auth/cloud-platform"],
  });
  const client = await auth.getClient();
  const url =
    `https://health.googleapis.com/v4/projects/${encodeURIComponent(projectId)}/subscribers` +
    `?subscriberId=${encodeURIComponent(subscriberId)}`;

  const body = {
    endpointUri,
    endpointAuthorization: { secret },
    subscriberConfigs: [
      {
        dataTypes: ["steps", "distance", "floors", "sleep", "weight", "heart-rate", "exercise"],
        subscriptionCreatePolicy: "AUTOMATIC",
      },
    ],
  };

  const res = await client.request({ url, method: "POST", data: body });
  console.log(JSON.stringify(res.data, null, 2));
  if (res.data?.name && !res.data?.done && res.data?.metadata) {
    console.error(
      "\nNote: API returned a long-running Operation. Poll the operations API or re-check subscriber state in Cloud Console until done === true."
    );
  }
}

main().catch((e) => {
  const err = e?.response?.data ?? e?.message ?? e;
  console.error(typeof err === "string" ? err : JSON.stringify(err, null, 2));
  process.exit(1);
});

I have also checked if handshake is failing but there are no request made to my server from google! 

Best Answer
0 Votes
2 REPLIES 2

Hi @sahilvelhal777 

Welcome to the community. 

Thanks for sharing your question. 

The error you're encountering is because the example you followed was based on a previous beta version of the API. This was changed in our latest release. To fix this, could you please refer to the updated technical details in our reference documentation here: EndpointAuthorization Reference. We will go through and update the example and documentation to reflect this change. Please let us know if this helps you resolve the issues!

Best Answer
0 Votes

Hi @sahilvelhal777 

I want to check in on the problem you reported.   It looks like you are missing the endpointAuthorization section of the endpoint.  The syntax should look similar to this

POST {{google_health_v4_host}}/projects/6183080339/subscribers?subscriberId=webhooks
Authorization: Bearer {{access_token}}
Accept: application/json

{
  "endpointUri": "<endpoint-URL>",
  "subscriberConfigs": [
    {
      "dataTypes": [
        "steps", "weight", "sleep"
      ],
      "subscriptionCreatePolicy": "AUTOMATIC"
    }
  ],
  "endpointAuthorization" : {
    "secret": "Bearer 618308034039-au5r1vjbnic6pp.apps.googleusercontent.com"
  }
}

Does this example help you get further?

Best Answer
0 Votes