Skip to content

https-outcalls: add flexible outcalls and pricing version 2 (interface spec 0.68.0) #385

Closed

Description

@marc0olo

The https-outcalls skill predates interface specification 0.68.0 (2026-09-14), which added flexible outcalls and a second pricing mechanism. Flexible outcalls have been live on mainnet since 2026-09-02 (feature flag enabled; both replica versions across all 42 subnets carry the commit), so an agent following the skill today writes code that will not compile against the current Rust CDK and prices calls with a deprecated mechanism.

What changed upstream

  • New management canister method flexible_http_request: a committee of nodes return their individual HTTP responses instead of the subnet reaching consensus on one. Optional replication argument (min_responses, max_responses, total_requests), and a result variant whose err arm carries a global_error (timeout, out_of_cycles, responses_too_large, too_many_rejects) plus per-node resource reports. Both arms arrive as a reply, not a reject.
  • New optional pricing_version field on http_request: 1 ("legacy", still the default, now deprecated) or 2 ("pay-as-you-go"). Version 2 prices the resources a call actually consumes rather than max_response_bytes, and the attached cycles double as the per-node budget the call runs within. Flexible outcalls have no pricing_version and are always priced with version 2.
  • New System APIs ic0.cost_http_request_v2 (takes a record of expected resource usage) and ic0.subnet_self_node_count. ic0.cost_http_request is deprecated along with the version it prices.
  • Non-replicated mode (is_replicated = false) is no longer experimental.
  • ic-cdk-management-canister 0.2.0 removed the free functions http_request, cost_http_request and http_request_with_closure (breaking). Replacements are the HttpRequest and FlexibleHttpRequest builders, which always select pricing version 2, plus with_expected_roundtrip_time_ms, with_expected_raw_response_bytes, with_expected_transformed_response_bytes and with_expected_transform_instructions to narrow the reservation from the worst case.

What is stale in the skill today

Line numbers are from the currently published SKILL.md.

  • The Rust implementation section does not compile against the current CDK. Cargo.toml pins ic-cdk = "0.19" (line 175) and the examples call ic_cdk::management_canister::http_request with struct literals containing is_replicated: None (lines 227, 310). The function is gone in ic-cdk-management-canister 0.2.0, and ic-management-canister-types 0.10 added fields that break exhaustive struct literals.
  • Pitfall 2 and the "Cycle Cost Estimation" section present version 1 as the pricing model (lines 36, 331, 350, 352): the ic0.cost_http_request API, the 49_140_000 + 5_200/request byte + 10_400/max_response_bytes byte formula, and the advice that "the way to lower the cost of an outcall is a tighter max_response_bytes". Under version 2, max_response_bytes no longer sets the price; it bounds the response and, through the worst-case usage computed from it, the size of the reservation.
  • Pitfall 7 ("Omitting max_response_bytes... charges cycles accordingly", ~20.85B cycles) is version 1 only.
  • No mode coverage. The skill describes replicated consensus only. Non-replicated mode appears just as is_replicated = null in code, and flexible mode is absent entirely, so an agent asked for a price feed will be steered into replicated mode, which is precisely the case where consensus fails.
  • The Motoko path diverges from Rust now. Call.httpRequest from the ic mops package still attaches the version 1 cost, so Motoko cannot reach version 2 or flexible mode through the wrapper yet. Setting pricing_version = 2 on a request passed to Call.httpRequest would attach the version 1 amount, far below what version 2 reserves, and the call would run within a budget too small to finish. That trap is worth a pitfall entry of its own.

Suggested additions

  • A modes section: replicated, non-replicated, flexible, with what the canister receives in each and which risk each one trades away.
  • A flexible outcall walkthrough: sizing the committee against ic0.subnet_self_node_count, handling any count between min_responses and max_responses (fewer than the maximum is a normal success), treating the responses as an unordered multiset that does not identify its nodes, the 2 MiB joint limit on the delivered responses (against 2 MB per individual response), PUT/DELETE/PATCH being allowed only when min_responses, max_responses and total_requests are all equal, and reconciling disagreement in canister code.
  • Pricing version 2: what it charges for, ic0.cost_http_request_v2, the with_expected_* reservation setters, and the fact that under-attaching beyond the base fee is accepted and shrinks the per-node limits rather than rejecting the call up front.
  • A new pitfall: the builders price through ic0.cost_http_request_v2, so a canister built with them needs a replica that provides that System API.

References

Activity

  1. marc0olo commented on Sep 23, 2026

    @marc0olo
    ContributorAuthor

    Done in #387: builder-based Rust examples on ic-cdk 0.20 / ic-cdk-management-canister 0.2, both pricing versions (references/pricing-version-2.md), a modes comparison (replicated, non-replicated, flexible), a flexible outcall walkthrough (references/flexible-outcalls.md), and the Motoko version 1 only constraint with the mismatched-funding pitfall. The replica requirement for cost_http_request_v2 is carried by the icp-cli >= 0.2.2 compatibility floor.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions