HWireline Data Exchange — partner API Skill: HWS_DATA_SKILL v1.5.19 (spec 1, evolving) Canonical: https://dx.horizontalwireline.ai/HWS_DATA_SKILL.md Index: https://dx.horizontalwireline.ai/skill.json Alias: https://dx.horizontalwireline.ai/skill.md MUST: at the start of every task GET /skill.json. If version differs from your copy, GET /HWS_DATA_SKILL.md and follow that. Never skip this check. Never trust a cached skill. v1 will keep evolving; skill.json is authoritative. Base URL: https://dx.horizontalwireline.ai OpenAPI: https://dx.horizontalwireline.ai/openapi.json Human/agent docs: https://dx.horizontalwireline.ai/docs Self-serve sample key: POST https://dx.horizontalwireline.ai/signup { "name", "email" } Returns { plaintext, prefix, expires_at, sample }. The secret is shown once and never emailed. Sample scope: customer Sample, three pads (Demo/East/South), two trucks each (99991–99996). The simulator runs a 73-minute wireline trip at 1 Hz: stage cycles RIH -> STANDBY at 9000 ft TD -> POOH -> STANDBY at surface (-30 ft) for one minute, then the next run-in. Depth and tension track the stage. Real jobs report operator-specific numeric stage codes, not these labels, so treat stage as an opaque string rather than an enum. Lost key: POST https://dx.horizontalwireline.ai/recover { "email" } then open the emailed link. Always answers 200 whether or not the address is registered. Works for self-serve and partner mailboxes. The secret is never emailed. Admin (privilege admin, super-user console mints it): POST https://dx.horizontalwireline.ai/v1/admin/intents { "action", "payload" } → 202 { id } Emails a 6-digit code to the acting admin. Confirm: POST https://dx.horizontalwireline.ai/v1/admin/intents/{id}/confirm { "code" } Actions: users.onboard, users.notify, users.roll_key, grants.create/update/revoke, recommendations.review, notify_rules.create/update/delete. Cannot grant privilege admin. Plaintext shown once on onboard/roll, never emailed. GET https://dx.horizontalwireline.ai/v1/admin/users (prefixes only) Auth: Authorization: Bearer hwx_ Never put the long-lived key in a query string. 401 = missing/invalid/expired/revoked key. 403 = key cannot see that customer/pad/truck, or lacks the privilege. 403 bodies do not list other tenants. Start: 1. ALWAYS GET https://dx.horizontalwireline.ai/skill.json at the start of every task 2. If version differs from your copy, GET https://dx.horizontalwireline.ai/HWS_DATA_SKILL.md and use that 3. GET https://dx.horizontalwireline.ai/openapi.json 4. GET https://dx.horizontalwireline.ai/v1/catalog 5. GET https://dx.horizontalwireline.ai/v1/logs 6. GET https://dx.horizontalwireline.ai/v1/logs/{id}/content (LAS 2.0 file; placeholder.href) or GET https://dx.horizontalwireline.ai/v1/logs/{id}/curves (downsampled plot series) 7. GET https://dx.horizontalwireline.ai/v1/telemetry/latest 8. GET https://dx.horizontalwireline.ai/v1/telemetry?from=&to= (max 7 days) Optional live: POST https://dx.horizontalwireline.ai/v1/stream-tokens then GET the stream_url Write (requires privilege write): POST https://dx.horizontalwireline.ai/v1/inbound Preferred: { "topic": "well-file", "payload": { "well": "8H", "stage": 37 } } Flat body: { "well": "8H", "stage": 37 } topic defaults to inbox Array: { "topic": "events", "payload": [ {…}, {…} ] } topic: optional, 1–128 chars [A-Za-z0-9._/-], default inbox payload: JSON object or array, max ~200 KiB 201 { id, topic, received_at }. Isolated from truck_telemetry. GET https://dx.horizontalwireline.ai/v1/inbound lists this key's posts (?limit=) Recommend (any valid key, not write): POST https://dx.horizontalwireline.ai/v1/recommendations { "message": "It would be great if you can share LAS files for every completed stage.", "context": "optional — what you were trying to do", "source": "optional — Cursor / Claude / tool name" } 201 { id, message, status, received_at }. HWireline reviews by hand. Not every suggestion ships. One concrete suggestion per post. Do not spam. This is not /v1/inbound and not a command to trucks. GET https://dx.horizontalwireline.ai/v1/recommendations lists this key's posts (?limit=) When a suggestion ships, the skill version in /skill.json bumps — that is why you always GET /skill.json first. Paging: /v1/telemetry returns cursor. It is opaque and encodes (time, truck_number). Send it back verbatim as ?cursor=…; never parse or synthesize one. cursor: null means the page you received is the last one. Live streams: The ticket from /v1/stream-tokens is short-lived (expires_in seconds). Automatic EventSource reconnects inside that window are fine. On 401, or on an "end" event carrying "remint", mint a new ticket — retrying the same stream_url will keep failing. Key expiry: Every key has expires_at. Self-serve sample keys last 30 days. An expired or revoked key answers 401 on every endpoint. POST /recover emails a one-hour reset link to the party's mailbox (self-serve and partner). Opening it mints a replacement; the secret is shown once in the browser, never in the email. Always 200 so the response does not reveal whether the address is registered. Rules: You only see granted names. Empty job context is hidden. raw_payload is never returned. Rate limits: 429 + Retry-After. Limits apply per IP before auth and per key after, so back off on 429 instead of rotating keys. X-RateLimit-Scope says which tier you are seeing, and a 429 always reports the tier that blocked you. Do not brute-force customer names. Use catalog. History before a grant's valid_from is hidden unless your grant sets include_history. It is a property of the grant, not a query parameter; sending ?include_history= does nothing. The sample tenant already has it enabled, so you can read sample history from before you signed up. Filters on /v1/stream-tokens are checked against your grants, so an out-of-scope customer/pad/truck is a 403 at mint time, not a silent empty stream. An empty stream means the well is quiet. Stage Share (skill 1.5.19, contract wellscope.stageshare/1.0.0): This is a live WellScope stage overlay (depth-aligned LAS + pins + cursors), not truck telemetry and not GET /v1/logs. Privilege stageshare is NOT write. Do not post stage-share JSON to /v1/inbound. Do not put hwx_ in guest URLs. Header: WellScope-Contract: wellscope.stageshare/1.0.0 Create 201 includes { contract: { accepted: "1.0.0" } }. Persist that. Same major/newer minor: DX adapts. Unknown major: 422 contract_unsupported. When mail.sender is dx, DX sends invite email (template stageshare_invite) from ecodrive_notifications; the truck only forwards {invites:[{email}]}. Guest page: https://horizontalwireline.ai/stageshare/ss_… then POST /v1/stageshare/sessions { share_key, display_name } → sst_ ticket (cabin Copy). Invite ss_ links mint with no name (identity is the invited email). Overlay: /v1/stageshare/stream?token=sst_… (not /v1/stream). Room LAS GET …/rooms/{id}/logs → {files}; columns tally|live|rih|corr|pump|pooh. Cap 48 objects. POST …/logs reuses by filename or the live slot (reuse:true). PATCH …/logs/{lid} {visible} guests+ops (live ok). Full reassign frozen only; live → 409. Relabel hides the previous occupant (visible:false). Promote keeps the prior file visible. POST …/logs/sync diffs Pi inventory. POST …/logs/sync/request (admin) → WS sync-needed, or 409 cabin_offline. POST …/logs/{lid}/promote copies live→frozen (preview + span; expect_last_ft; live pins move). PUT …/content replaces a frozen file (resend / reprocess). See /STAGESHARE_LOGS.md. Pi must POST …/pointer at least every 30s while the room is live, including surface/standby (negative MD, speed 0). EcoDrive telemetry does not count. 2 minutes quiet → truck offline (room stays live). Optional live_tail on that pointer is the short CCLR lead past the last LAS flush (≤ ~10 ft, cap 80). See /STAGESHARE_UPLINK.md (skill.json urls.stageshare_uplink). DELETE frozen logs (not live). GET …/sessions and GET …/presence → {sessions, presence} (no tokens). Finalize keeps a recorded snapshot for 5 days, then files are deleted. GET …/snapshot is a zip. Comments: JSON array; body or text; column_id or column. Chat omits depth_ft; pins set depth_ft. See /STAGESHARE_THREAD.md (skill.json urls.stageshare_thread). Survey: include survey on POST /rooms (preferred). PUT/GET …/rooms/{id}/survey for late attach. Ground TVD (0 = surface). Guest 2D above chat, 3D on click, MD bubble. See /STAGESHARE_SURVEY.md (skill.json urls.stageshare_survey). Pointers: {depth_ft, column_id, x_frac} in well space. Reject css_px, x, y. Guest Follow is view_hint.tool_depth_ft (keep-in-view), not gold Edge/cabin pointer-move. Chat: own rows right/gold; others left. Depth pins: HWS maroon #820024 contour. CCLF ±0.75 V. Agents with a guest ss_ link: POST /v1/stageshare/sessions, then logs + comments. Agents with stageshare: GET /v1/stageshare/rooms, then logs/content/comments. Sample keys do not have stageshare. MCP has no Stage Share write tools.