Skip to content

Authentication

Every incoming webhook is protected. Before a scanner, SIEM or pipeline can post to one, it needs an API application in the platform and a bearer token obtained with that application's credentials.

Navigation

Configuration > Integrations > Halo API on the GRC-ITSM website navigation.


1. Create the API application

Create one application per integration, not one shared across all of them. Separate applications give you separate credentials to rotate, separate permissions to scope, and a distinguishable actor in the audit trail.

  1. Go to Configuration > Integrations > Halo API.
  2. Add a new application.
  3. Set the authentication method to Client ID and Secret.
  4. Record the Client ID and Client Secret. The secret is shown once, so store it in your secrets manager immediately.
  5. Grant only the module permissions the integration needs. An alert-filing pipeline does not need read access to billing.

The application is the actor in your audit trail

Every ticket an incoming webhook creates is attributed to the API application that authenticated the call. Give it a name that identifies the integration (GuardDuty Alert Feed, not API User 3), because that name is what an assessor sees when they ask who filed a piece of evidence.


2. Get a token

The API uses the OAuth2 client credentials grant. Exchange your client id and secret for an access token:

curl -X POST "https://<your-tenant>.halopsa.com/auth/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials" \
  -d "client_id=<your client id>" \
  -d "client_secret=<your client secret>" \
  -d "scope=all"

The response contains an access_token.

Tokens expire after about an hour

Fetch a token, use it, and fetch a new one when it expires. Do not hardcode a token. It will stop working roughly an hour after you paste it.

This is the single most common cause of an integration that works on the day it is built and fails the next morning. If your sending system can only attach fixed, static headers and cannot run a token request first, it cannot use this authentication method, and you should raise that before building the rest of the integration.

Restrict the scope below all where your integration allows it.


3. Call the webhook with the token

Send the token as a bearer token on every webhook call:

curl -X POST "<webhook URL>" \
  -H "Authorization: Bearer <access_token>" \
  -H "Content-Type: application/json" \
  -d '{ "site_id": 1, "summary": "Example alert" }'

Finding the webhook URL

Each incoming endpoint has its own URL, and the URLs are specific to your tenant.

  1. Go to Configuration > Integrations > Custom Integrations > Integration Runbooks.
  2. Open the runbook for the endpoint you want.
  3. On the Details tab, scroll to Runbook Start Access. Under the heading "This runbook can be started by doing a POST to the following URL", the page shows the URL. Copy it exactly.

Three fields on that panel need to read as follows, and the URL only exists at all when the first one does:

Field Required value
Runbook Start Access Can only be started from Halo and from a public endpoint
Authentication Halo API Bearer Token
Initial Webhook Verification None

If Runbook Start Access reads "Can only be started from Halo", there is no inbound URL and the runbook cannot be called externally until that is changed.

The page also notes that ticket variables cannot be used when a runbook is started from this URL. That does not affect these three endpoints, which take everything they need from the JSON body, but it is worth knowing if you build your own.

Initial Webhook Verification

Leave this set to None. It exists to answer a challenge-response handshake that some providers send when you register a subscription with them; Slack and Microsoft Graph do this. A scanner or pipeline posting evidence never issues such a challenge, so enabling it only adds a step nobody performs.


Handling failures

Response Meaning What to do
401 / 403 Token missing, expired, or the application lacks permission Fetch a fresh token. If it still fails, check the application's module permissions.
404 Wrong URL, or the runbook's start access does not expose a public endpoint Re-copy the URL from the runbook page and confirm its start access setting.
202 but the record never appears The call authenticated and was accepted, but the payload was rejected during processing Open the runbook's Log tab, which names the failing step and its rejection code, then check the endpoint page's rejection table.

A rejected payload is not an authentication problem. A 202 means your credentials worked and the request was accepted; it says nothing about whether the write succeeded. See Responses.


Security considerations

  • Rotate the client secret on the schedule your organization requires, and immediately if it may have been exposed. Rotating one integration's secret does not affect the others, which is the reason for separate applications.
  • Never put the secret in a URL, a query string, or a webhook payload. It belongs in the token request body and nowhere else.
  • Use HTTPS for every call, including the token request.
  • In a FedRAMP boundary, an external system posting into the platform is an information exchange that must be documented. See the interconnection guidance on the Integrations overview before enabling a new sender.