Loading...
Loading...
Serve inbound HTTP from a Motoko canister (webhooks, curl POST/GET, bot callbacks, ingest/health APIs) via the IC HTTP Gateway methods `http_request` / `http_request_update`. Load whenever the user or spec needs a canister URL that external HTTP clients can hit — Stripe/GitHub/Slack webhooks, Telegram bots, health checks, or any plain REST-ish endpoint. NOT for calling external APIs from the canister (use `extension-http-outcalls`); NOT for certified static asset serving.
npx skill4agent add caffeinelabs/skills extension-http-endpointsmo:caffeineai-http-endpoints| Need | Skill |
|---|---|
External client ( | this skill |
| Canister calls an external HTTPS API | |
| Typed OpenAI / Google / X / Stripe client | the matching domain skill (those wrap outbound calls) |
http_requestCall.httpRequest*.caffeine.xyzhttp_requestHttp.upgrade()http_request_updatehttps://<backend-canister-id>.icp0.io/....ic0.apphttps://<backend-canister-id>.raw.icp0.io/...module {
public type Request = { method : Text; url : Text; headers : [HeaderField]; body : Blob; certificate_version : ?Nat16 };
public type Response = { status_code : Nat16; headers : [HeaderField]; body : Blob; streaming_strategy : ?StreamingStrategy; upgrade : ?Bool };
public func path(url : Text) : Text;
public func header(req : Request, name : Text) : ?Text; // case-insensitive
public func text(status : Nat16, body : Text) : Response;
public func upgrade() : Response; // query path asks gateway to call http_request_update
public func notFound() : Response;
public func methodNotAllowed() : Response;
public func bodyText(req : Request) : ?Text; // null if body is not UTF-8; HMAC must use req.body
};mixin (
onQuery : Http.Request -> Http.Response,
onUpdate : Http.Request -> async Http.Response,
) {
public query func http_request(req : Http.Request) : async Http.Response;
public func http_request_update(req : Http.Request) : async Http.Response;
};mops add caffeineai-http-endpointsmain.moMixinAuthorizationhttp_request*onQueryonUpdateHttp.upgrade()import Http "mo:caffeineai-http-endpoints/Http";
import MixinHttpEndpoints "mo:caffeineai-http-endpoints/MixinHttpEndpoints";
import AccessControl "mo:caffeineai-authorization/access-control";
import MixinAuthorization "mo:caffeineai-authorization/MixinAuthorization";
actor {
let accessControlState : AccessControl.AccessControlState;
include MixinAuthorization(accessControlState, null);
func onHttpQuery(req : Http.Request) : Http.Response {
let path = Http.path(req.url);
if (req.method == "POST" and path == "/webhook") {
return Http.upgrade();
};
if (req.method == "GET" and (path == "/" or path == "/health")) {
return Http.text(200, "ok");
};
Http.notFound();
};
func onHttpUpdate(req : Http.Request) : async Http.Response {
let path = Http.path(req.url);
if (req.method == "POST" and path == "/webhook") {
// Signature checks: Http.header(req, "stripe-signature"), HMAC over req.body
ignore Http.bodyText(req);
return Http.text(200, "received");
};
Http.notFound();
};
include MixinHttpEndpoints(onHttpQuery, onHttpUpdate);
};import AccessControl "mo:caffeineai-authorization/access-control";
module {
type NewActor = {
accessControlState : AccessControl.AccessControlState;
};
public func migration(_old : {}) : NewActor {
{ accessControlState = AccessControl.initState() };
};
};onUpdateHttp.upgrade()http_request.raw.icp0.ioHttp.headerreq.bodycurl503backend_response_verificationCertification values not foundHttp.upgrade()# Reads — must be raw
curl https://<backend-canister-id>.raw.icp0.io/health
# Writes — non-raw is fine (update path)
curl -X POST https://<backend-canister-id>.icp0.io/webhook \
-H 'content-type: application/json' \
-H 'x-webhook-secret: …' \
-d '{"event":"…"}'?canisterId=<id>https://icp0.io/...localhost?canisterId=204extension-http-outcallsextension-stripecurlpocket-iccurl@dfinity/picmakeLive()mopshttp://<canister-id>.raw.localhost:<port>/healthhttp://127.0.0.1:<port>/notes?canisterId=<id>http://<canister-id>.localhost:<port>/health.raw503*.icp0.io*.raw.icp0.io