Skip to content

feat: add baselinker-webhooks skill - #181

Merged
garethx merged 2 commits into
hookdeck:mainfrom
garethx:feat/baselinker-webhooks
Aug 14, 2026
Merged

feat: add baselinker-webhooks skill#181
garethx merged 2 commits into
hookdeck:mainfrom
garethx:feat/baselinker-webhooks

Conversation

@garethx

@garethx garethx commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Adds the baselinker-webhooks skill for BaseLinker (rebranded Base.com), a Polish multichannel e-commerce / warehouse / order-management platform. BASELINKER was added to the Hookdeck source-type enum ~2026-08-10.

Why this one is unusual

BaseLinker publishes no webhook documentation at all. Before authoring I confirmed:

  • api.baselinker.com (= api.base.com) is 195 request/response methods over connector.php. No webhook registration/subscription method exists; change tracking is polling (getJournalList, getOrderReturnJournalList, getInventoryProductLogs).
  • The developers.baselinker.com/shops_api "shops data exchange protocol" is BaseLinker calling merchant-hosted PHP scripts with a shared password — RPC polling, not signed event delivery.
  • Neither the English nor Polish Help Centre documents an outbound webhook or a "send HTTP request" automatic action. Every webhook hit is an inbound marketplace/courier integration (Heureka, Fruugo, ClickPost, Blue Dart, Myntra).
  • No developer portal exists (base.com/*/developers 404s).
  • hookdeck.com/docs/sources lists BASELINKER with no documentation link.

So the skill is authored from primary evidence rather than provider prose.

The scheme

Two properties make this unlike any other skill in the repo, and both drive every example:

  1. Deliveries are HTTP HEAD, never POST. A HEAD request has no body — handlers use app.head() / an exported HEAD / @app.head, and never read req.body. No JSON body parser is mounted on the route.
  2. The entire payload is in the query string. Values are always strings, so order_id is coerced and validated and nothing is assumed present.

There is no signature verification — no HMAC, no secret, no timestamp/replay check, no handshake. Confirmed in Hookdeck's own API spec, where SourceConfigBaselinkerAuth is {properties: {}, additionalProperties: false} (accepts no secret), while every HMAC source there carries webhook_secret_key. BaseLinker sits in the zero-property-auth cohort with AWS SNS, Microsoft Graph, Microsoft SharePoint, Monday, Strava, Tikkie, Ethoca and Zift.

No verifier is fabricated. The only auth shown is an optional token the user appends to their own endpoint URL, labelled throughout as not provider authentication. X-BLToken is documented as the outbound request header for your own API calls and explicitly not a webhook signature.

Responses stay bodyless per RFC 9110 §9.3.2, including the 400/401 paths — hence Response(status_code=...) rather than HTTPException in FastAPI. Worth knowing: because a HEAD response can't carry a body, Hookdeck returns the request id in the x-hookdeck-request-id header instead.

Things a reviewer may otherwise re-flag

  • hmac.compare_digest / crypto.timingSafeEqual appear in the examples. These are constant-time comparisons of the user's own URL token, not a provider signature check. Please don't read them as a fabricated verifier.
  • order_id / state are hedged deliberately. They are presented as observed examples, never as a documented or exhaustive parameter list. state is treated as an opaque string, not an enum and not an event-type discriminator — there is no switch over state values anywhere, by design.
  • Automatic Actions system-event names (order fetched, status set, shipment created, …) are referenced as background context only. They are panel UI labels, not wire values, and are not used as event-name constants.
  • The exact panel path that registers the callback URL is unconfirmed and flagged as such in TODO.md.

Testing

  • ./scripts/validate-provider.sh baselinker-webhooks — passes
  • express: 23 tests pass
  • nextjs: 19 tests pass
  • fastapi: 22 tests pass (fresh venv from requirements.txt)

Open items are tracked honestly in skills/baselinker-webhooks/TODO.md.

🤖 Generated with Claude Code

https://claude.ai/code/session_01VcD5gC7udDGBQD8Ko97q8i

garethx and others added 2 commits August 14, 2026 10:10
BaseLinker (rebranded Base.com), a Polish multichannel e-commerce, warehouse
and order-management platform. Added to Hookdeck's source-type enum ~2026-08-10.

BaseLinker publishes NO webhook documentation at all, so this skill was authored
from primary evidence rather than provider docs:

- api.baselinker.com is 195 request/response methods over connector.php, with
  change tracking by polling (getJournalList). No webhook registration method.
- Neither the English nor Polish Help Centre documents an outbound webhook or a
  "send HTTP request" automatic action. Every "webhook" hit is an inbound
  marketplace/courier integration.

Two properties make it unlike any other skill here, and both drive the examples:

1. Deliveries are HTTP HEAD, never POST. A HEAD request has no body, so handlers
   use app.head() / an exported HEAD / @app.head and never read req.body.
2. The entire payload is in the query string. Values are always strings, so
   order_id is coerced and validated; nothing is assumed present.

There is NO signature verification: no HMAC, no secret, no timestamp, no
handshake. This is confirmed by Hookdeck's own spec, where
SourceConfigBaselinkerAuth is an empty object accepting no properties, while
every HMAC source there carries webhook_secret_key. No verifier is fabricated.
The only auth shown is an OPTIONAL token the user appends to their own endpoint
URL, labelled throughout as not being provider authentication.

Responses stay bodyless per RFC 9110 section 9.3.2, including the 400/401 paths
(hence Response(status_code=...) rather than HTTPException in FastAPI).

Hedged honestly: order_id and state are presented as observed examples, not a
documented or exhaustive parameter list, and state is treated as an opaque
string rather than an event-type discriminator. See TODO.md.

Testing: validate-provider.sh passes; 23 express + 19 nextjs + 22 fastapi tests
pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VcD5gC7udDGBQD8Ko97q8i
Self-review caught two claims that ran past the evidence. Both were assertions
about the BaseLinker panel, which was never inspected: this skill was written
without account access, so only the documentation was ever searched.

- setup.md claimed the panel "has no Webhooks settings page" and that no
  endpoint registry, secret screen or test button "exists". Rewritten to claim
  only what was checked (both Help Centre locales, exhaustively), with an
  explicit note that the panel was not inspected and beats any inference here.
- setup.md step 2 presented Automatic Actions as the place to wire the outbound
  call, hedging the placeholder syntax but not the capability. BaseLinker
  documents no URL-calling action at all, so the step now says plainly that it
  cannot be specified, and points at getJournalList polling as the supported
  path if the panel offers no such action.
- verification.md said there is "nowhere in the panel to create" a signing
  secret. Replaced with the citable form: none is documented, and Hookdeck's
  source config accepts no secret.

Also records in TODO.md that no account was available, since that is the root
cause of the whole class.

Verified while reviewing, both now confirmed verbatim against the live docs and
left unchanged: the 100 requests/minute rate limit, and getJournalList's 3-day
window, last_log_id cursor and account-settings prerequisite.

No code changes. validate-provider.sh passes; 23 express + 19 nextjs + 22
fastapi tests still pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VcD5gC7udDGBQD8Ko97q8i
@garethx
garethx marked this pull request as ready for review August 14, 2026 10:56
@garethx
garethx merged commit 09de3a9 into hookdeck:main Aug 14, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant