Oracle Cloud is the least written-about of the four and the one where the documentation is least likely to match what you actually receive. This post is about the two places it differs from the others in ways that matter: how alarms reach you, and how requests are authenticated.

Alarms arrive through Notifications

OCI Monitoring alarms do not post directly. They publish to a Notifications topic (ONS), which delivers to a subscription — the same indirection as SNS on AWS, including the subscription confirmation handshake.

graph LR M["OCI Monitoring
alarm"] --> T["Notifications
topic"] T --> S["HTTPS
subscription"] S --> W["/api/webhooks/oci"] S -.->|"first message"| C["ConfirmationURL"] C -.->|"reported,
never fetched"| P(["a person confirms it
once they recognise the topic"]) W --> N["One normalized incident"] style P fill:#fff4e2,stroke:#f4a03a,stroke-width:2px style N fill:#f4f6ff,stroke:#a78bfa

Same rule as SNS, for the same reason: the ConfirmationURL is reported rather than fetched. Auto-confirming would let anyone who can reach the endpoint make the API issue a GET to a host of their choosing, and no amount of "but it's from Oracle" in the payload changes that — the payload is exactly the thing an attacker controls.

The payload, and the transition field

The field that carries the state change is type, not a status:

ValueMeansHandled as
OK_TO_FIRINGThe alarm just startedOpens an incident
REPEATStill firing, periodic reminderDeduplicated onto the open one
FIRING_TO_OKClearedResolves the incident
RESETAlarm resetResolves the incident

The alarm itself sits in alarmMetaData[0] — an array, with the OCID, the MQL query, the namespace, the dimensions and a link back to the console. Dimensions become labels, and resourceDisplayName becomes the service, which is what match rules bind to.

Timestamps are epoch milliseconds

timestampEpochMillis is exactly what it says. Handed to a parser that assumes seconds it produces a date in the year 55,000 — which sorts strangely, does not error, and is the kind of bug you find three weeks later while wondering why one incident is always at the top of the list. Opsgenie's createdAt has the same shape and the same trap.

Request signing, done by hand

OCI does not issue bearer tokens. Every request is signed with your API key over a named set of headers — the HTTP Signatures draft. It is the fussiest of the four auth schemes and the most clearly specified.

What gets signed: the request target, host and date for a GET; plus content length, content type and a SHA-256 of the body for a POST. That last part is what stops a signed request being replayed with different contents.

(request-target): post /20180401/metrics/actions/summarizeMetricsData?…
host: telemetry.{{region}}.oraclecloud.com
date: Fri, 26 Sep 2026 11:04:22 GMT
content-length: 184
content-type: application/json
x-content-sha256: Zm9vYmFy…

→ RSA-SHA256 over the above, base64, into:
Authorization: Signature version="1",keyId="{tenancy}/{user}/{fingerprint}",
              algorithm="rsa-sha256",headers="…",signature="…"

Roughly forty lines with cryptography, which the image already carries for its own JWTs — so no OCI SDK. Five environment variables: OCI_TENANCY, OCI_USER, OCI_FINGERPRINT, OCI_KEY_FILE, OCI_REGION. Missing any one, the steps report unavailable and name what is absent rather than failing obscurely inside a signature.

Two read steps

StepWhat it does
oci.metricsAn MQL query against Monitoring — CpuUtilization[1m].mean() in a namespace and compartment.
oci.logsAn OCI Logging search over a window.
cloud.healthProvider-side issues, filtered to your region. See below — this one had a surprise in it.

The status feed is not a status page

This is the one part of the OCI connector verified against the live service rather than against documentation, because the feed is public — and it was not what we expected.

Most providers expose a Statuspage-style incidents.json. OCI does not. It publishes components.json: a single document containing every region — ninety of them — each with every service and that service's current status. There is no flat list of open incidents to read.

graph TB F["components.json
~90 regions"] --> R{"Filter to
OCI_REGION"} R -->|"region matches"| S["walk its serviceHealthReports"] R -->|"no such region"| E["report it and skip —
check OCI_REGION"] S --> Q{"serviceStatus"} Q -->|"NormalPerformance"| I["ignore"] Q -->|"anything else"| O["an open event, with
the service and status"] style E fill:#fff4e2,stroke:#f4a03a style O fill:#fde8e8,stroke:#e57373

So the step filters to the region the deployment actually runs in. Without that filter an incident in Sydney would be reported to someone in Ashburn as though it were theirs — which is worse than saying nothing, because it sends an engineer to blame the provider for an outage on another continent. A region name that does not appear in the feed is reported as a configuration problem rather than silently returning "all clear".

Getting it wired

  1. A Notifications topic with an HTTPS subscription to /api/webhooks/oci?token=…. Confirm it by hand from the first message.
  2. Point your alarms at the topic and give them names you would be happy to see as alert names, because that is what they become.
  3. An API key for a read-only user, with a policy allowing read metrics and read log-content in the compartments you care about.
  4. Set OCI_REGION to the canonical name — us-ashburn-1, not "Ashburn". The health step validates it and will tell you if it is wrong.

Stated plainly

The alarm normalizer and the two read steps are built to Oracle's published APIs and tested through the product, but not against a live tenancy. The signing implementation follows the documented scheme; the first real request is the test, and a signature failure is loud rather than subtle. The health step is verified live.

Next in this series

One on-call rota, four clouds — what stays the same across all of them, what genuinely differs, and where ServiceNow fits underneath.

Keep reading