# Norway Havtil Offshore Safety Monitor (`chosen_loafer/havtil-offshore-safety-monitor`) Actor

Tracks Havtil's (Norwegian Ocean Industry Authority) official supervision feeds for offshore oil and gas: new and changed audit reports, investigation reports, consents, and orders. Company/facility data only, no personal data.

- **URL**: https://apify.com/chosen_loafer/havtil-offshore-safety-monitor.md
- **Developed by:** [Pavel Staurouski](https://apify.com/chosen_loafer) (community)
- **Categories:** Business, News, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $20.00 / 1,000 checked report records

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Norway Havtil Offshore Safety Monitor

Tracks **Havtil** (the Norwegian Ocean Industry Authority, formerly PSA Norway) -
Norway's offshore oil-and-gas safety regulator - for new and changed supervision
records: audit reports, investigation reports, consents, and notices about orders.

### What you get

One record per new or changed report this run, tagged with an explicit `matchState`:

- `FIRST_SEEN` - your own baseline run.
- `NEW` - a report not seen on a previous run.
- `AMENDED` - a previously-seen report whose title, introduction, publication date, or
  category tags changed.
- `UNCHANGED` records are never pushed or charged.

Each record carries the report's title, Havtil's own one-line introduction, the full
article-body summary (objective/result/follow-up for an audit, operator/well/field for
a consent, and so on), Havtil's own topic category tags, the publication date, and a
direct link back to Havtil's own page.

### Scope: one feed per run, four feeds covered

Havtil publishes five structured supervision sub-feeds. This Actor covers **four** of
them - Audit reports, Investigation reports, Consents, and Notices about orders - all
four sharing one listing/detail page structure. Pick which one to track with the
`feedType` input (default: Audit reports); run the Actor again with a different
`feedType` to track another feed, each with its own independently tracked state.

**Acknowledgements of Compliance (AoC) is not covered.** It is a status registry
(grouped by facility, not a dated chronological feed) with a structurally different page
shape and no pagination - see `NOTES.md` for the full reasoning. A future version could
add it as a separate `feedType` option if there is buyer demand.

### No personal data

Every detail page on this source carries a "Contact"/"Pressekontakt" component naming an
individual Havtil press officer with a direct phone number and email address. This
Actor's parser only ever selects the page's `.article-body` element - a separate part of
the page - so that component's name, phone, and email are never read into any variable,
anywhere, not even transiently. Report titles name only corporate operators and
facilities (Equinor, Aker BP, OKEA, Halliburton, Saipem, Noble Drilling, Archer, and
similar), never individuals. As a second, defense-in-depth layer, every free-text field
this Actor does output (title, introduction, summary) is scanned for an email-address
pattern before being allowed into a record, and the whole field is withheld (never
partially redacted) if it ever matches. A final key-based guard
(`_shared/pii.js`'s `stripPersonalData`) is also applied to every assembled record
before it is pushed, as a structural safety net against a future code change
accidentally reintroducing a contact-shaped field.

### How it works

Havtil's supervision feeds are plain server-rendered HTML with simple `?p=N`
pagination, sorted newest-first and going back many years (confirmed live to `?p=80`
and beyond, still returning real, distinct, older reports every time). This Actor
paginates backwards from page 1, stopping once it hits a page shorter than a full page,
once a run of consecutive unchanged reports suggests it has caught up with where the
last run left off, or once it hits the `maxChecked` cap - whichever comes first.
Unchanged reports are detected from the cheap listing page alone; only new or changed
reports trigger a (billed) detail-page fetch for the full summary text.

### Why you might need this

HSE, compliance, and third-party-risk teams at operators, drilling contractors, and
subcontractors on the Norwegian continental shelf, plus insurers and law firms covering
Norwegian offshore risk, currently track Havtil's supervision activity by manually
re-checking its site. This Actor surfaces new and changed audit reports, investigation
reports, consents, and orders directly from Havtil's own feed, with optional watchlist
filtering down to the operators or facilities you actually track.

**Scope**: Havtil (Norway) only. Other national offshore-safety regulators are not
covered.

### Input

```json
{
  "feedType": "audit-reports",
  "watchlist": ["Equinor", "Aker BP", "Draugen"],
  "maxResults": 100,
  "maxChecked": 300
}
```

- `feedType` (optional, default `audit-reports`): which feed to track - `audit-reports`,
  `investigation-reports`, `consents`, or `important-messages` (Notices about orders).
- `watchlist` (optional, default empty): operator/company or facility names. Every
  record gets a `matchesWatchlist` flag (case-insensitive substring match against the
  title and category tags) so you can filter to the operators/facilities you actually
  track. Leave empty to track every record in the selected feed.
- `maxResults` (optional, default 100): caps how many new/changed records are pushed
  and charged in a single run. On the very first run this bounds how much of the feed's
  history is backfilled at once; the rest follows automatically on later runs.
- `maxChecked` (optional, default 300): caps how many listing records this run reads
  while paginating backwards. The feed goes back many years, so this bounds pagination
  depth, not just results - checking a record against prior state is free, only a
  new/changed one triggers a billed detail-page fetch.

### Sample output

One real report (fetched live from Havtil's own audit-reports feed, 2026-10-13), run
through this Actor's own `sanitiseListingItem()`/`mergeDetailRecord()` field mapping as a
first-ever run against that report (hence `matchState: "FIRST_SEEN"`; a later run against
an already-seen, unchanged report would never be pushed at all, and a changed one would
report `AMENDED` instead):

```json
{
  "recordKey": "audit-reports:2026/totalenergies-as-and-seadrill---atla-byggve-and-skirne---planning-and-implementation-of-permanent-plugging-and-abandonment-of-wells",
  "feedType": "audit-reports",
  "title": "TotalEnergies AS and Seadrill - Atla, Byggve and Skirne - planning and implementation of permanent plugging and abandonment of wells",
  "introduction": "The Norwegian Ocean Industry Authority (Havtil) has conducted an audit of TotalEnergies and Seadrill regarding the planning and implementation of permanent plugging and abandonment of the wells at Atla, Byggve and Skirne.",
  "categories": ["Well integrity"],
  "publishedAt": "2026-10-02T07:11:58.000Z",
  "sourceUrl": "https://www.havtil.no/en/supervision/audit-reports/2026/totalenergies-as-and-seadrill---atla-byggve-and-skirne---planning-and-implementation-of-permanent-plugging-and-abandonment-of-wells/",
  "checkedAt": "2026-10-13T12:00:00.000Z",
  "summary": "The audit was conducted from 11 May to 8 June 2026. Objective The objective of the audit was to determine whether TotalEnergies and Seadrill are complying with both regulatory requirements and their own requirements with regard to the permanent plugging and abandonment (PP&A) of the wells at Atla, Byggve and Skirne. The audit was intended to help determine whether risks in the planning and implementation of PP&A operations had been adequately identified. Furthermore, we wanted to assess whether the planned permanent well barriers have been qualified and verified with regard to their location, length and robustness. Result We identified two non-conformities during the audit. These concerned: Deficiencies related to procedures for the dropping and cutting of casing (Seadrill) Outdated barrier drawings related to wireline cutting (TotalEnergies) We also identified two improvement points. These concerned: Work on pressurised systems (Seadrill) Inadequate qualification of a creeping formation as a barrier element (TotalEnergies) What happens now? We have asked the companies to report by 23 October 2026 on how the non-conformities will be addressed and for their assessment of the improvement points observed.",
  "matchState": "FIRST_SEEN",
  "changedFields": [],
  "matchesWatchlist": false
}
```

`matchesWatchlist` is `false` here because this particular report's title and category tags
don't contain any of the example watchlist's names (`Equinor`, `Aker BP`, `Draugen`) - a
record naming one of your watched operators or facilities gets `matchesWatchlist: true`
instead. No Contact/Pressekontakt field, per "No personal data" above.

### Change tracking across runs

Run this Actor on a schedule - weekly is a reasonable match for Havtil's own
supervision-report cadence. Each report's key fields are stored per Actor run (kept
separately per `feedType`, so switching feeds never collides with another feed's
baseline), so every run after the first reports only what actually changed.

### Pricing

Pay per run started, plus per new/changed report reported (unchanged reports are never
charged). See the Pricing tab for exact rates.

***

# Actor input Schema

## `feedType` (type: `string`):

Which Havtil supervision feed to track. All four share the same listing/detail page structure. Acknowledgements of Compliance (AoC) is a separate, non-chronological status registry and is not covered by this Actor yet (see README).

## `watchlist` (type: `array`):

Optional list of operator/company or facility names (e.g. \["Equinor", "Aker BP", "Draugen"]). Each record gets a matchesWatchlist flag (case-insensitive substring match either direction, checked against the title and category tags) so you can filter to the operators/facilities you actually track. Leave empty to track every record in the selected feed.

## `maxResults` (type: `integer`):

Caps how many new/changed records are pushed and charged in a single run (unchanged records are never pushed or charged). On the very first run this bounds how much of the feed's history is backfilled at once; anything beyond the cap is picked up by later runs. Default 100.

## `maxChecked` (type: `integer`):

Caps how many listing-page records this run reads while paginating backwards through the feed (the feed is sorted newest-first and goes back many years, so this bounds pagination depth, not just results). Each one checked against prior state is free; only new/changed ones trigger a detail-page fetch and a charge. Default 300.

## Actor input object example

```json
{
  "feedType": "audit-reports",
  "watchlist": [],
  "maxResults": 100,
  "maxChecked": 300
}
```

# Actor output Schema

## `havtilResults` (type: `string`):

Per-record results for this run.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "watchlist": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("chosen_loafer/havtil-offshore-safety-monitor").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "watchlist": [] }

# Run the Actor and wait for it to finish
run = client.actor("chosen_loafer/havtil-offshore-safety-monitor").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "watchlist": []
}' |
apify call chosen_loafer/havtil-offshore-safety-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,chosen_loafer/havtil-offshore-safety-monitor"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/cvrLjmcZmPseaFF2a/builds/9TbWBX9dXcAYMuzjp/openapi.json
