Package contract
The planned Hex package is :nordic_devhouse. A supervised client owns transport state, returns tagged tuples, and emits :telemetry events without requiring Phoenix.
| Distribution | Namespace or import | Runtime |
|---|---|---|
| :nordic_devhouse | NordicDevhouse | Supported Elixir and OTP release lines |
Create and reuse the client
Add the named client to the supervision tree and pass its name to resource modules. Run returns {:ok, result} or {:error, error}; retry and polling stay inside the client process.
children = [
{NordicDevhouse.Client,
name: MyApp.NordicDevhouse,
api_key: System.fetch_env!("NORDIC_DEVHOUSE_API_KEY"),
max_retries: 3}
]
Supervisor.start_link(children, strategy: :one_for_one)
{:ok, result} =
NordicDevhouse.Scrape.run(MyApp.NordicDevhouse, %{
url: "https://example.com/products",
formats: [:json],
idempotency_key: "catalog-2026-08-11"
})
IO.inspect({result.job_id, result.records})Control a durable job
Start and wait expose the durable job ID for Oban and other workflows. Caller timeouts stop waiting but do not terminate the server-side job.
{:ok, job} =
NordicDevhouse.Scrape.start(MyApp.NordicDevhouse, %{
url: "https://example.com/products",
formats: [:json]
})
{:ok, result} =
NordicDevhouse.Jobs.wait(
MyApp.NordicDevhouse,
job.id,
timeout: :timer.minutes(10)
)Public models
Names follow the language conventions, but every SDK preserves the same fields and job-state semantics.
| Concept | Contract |
|---|---|
| Client options | API key, base URL, request timeout, retry budget, and optional test transport |
| Scrape request | URL, output formats, webhook URL, metadata, and idempotency key |
| Job | Stable ID, status, timestamps, progress, and warnings |
| Job result | Job ID, record count, typed data, metadata, and request ID |
| Webhook event | Verified event ID, type, timestamp, and typed data |
Handle errors idiomatically
Failures return {:error, %NordicDevhouse.Error{}} with kind, code, request_id, retry_after, status, and cause. Exceptional exits are reserved for invalid process configuration.
case NordicDevhouse.Scrape.run(client, request) do
{:ok, result} ->
consume(result)
{:error, %{kind: :rate_limited} = error} ->
Logger.warning("rate limited",
request_id: error.request_id,
retry_after: error.retry_after)
{:error, %{kind: :validation, issues: issues}} ->
reject(issues)
{:error, error} ->
Logger.error("SDK failure", request_id: error.request_id)
endVerify the raw webhook body
Verification uses the exact request bytes, validates HMAC-SHA256 and timestamp tolerance, and returns a typed event. Persist the verified event ID to prevent duplicate processing.
with {:ok, event} <-
NordicDevhouse.Webhooks.verify(
raw_body,
List.first(get_req_header(conn, "nordic-signature")),
System.fetch_env!("NORDIC_WEBHOOK_SECRET")
) do
import_result(event.data.job_id)
endRuntime and lifecycle rules
- 1Run the client under a supervisor with a stable registered name
- 2Return tagged tuples and structured exception-free request errors
- 3Emit documented :telemetry start, stop, retry, and exception events
- 4Keep Phoenix and Oban integrations optional
- 5Accept a behaviour-based transport adapter only at the internal test seam