This is how Binions calls out to a gRPC service. When a playbook needs to talk to an internal microservice or any API that speaks gRPC instead of REST, it uses the same webhookcaller daemon — you just set protocol: grpc and name the service and method. Binions figures out the message format from the server itself, turns your YAML into a proper protobuf request, makes the call, and hands the reply back as plain JSON. You never compile a .proto file or generate a client.
Good to know. This is the outbound client — Binions calling a gRPC service you point it at. To let a browser or external client reach Binions over gRPC, that is an inbound edge route instead; see HTTP edge & realtime.
| What it does | Calls a unary method on any reflection-enabled gRPC service |
| Playbook verb | webhook.send with protocol: grpc — no new vocabulary |
| How it learns the schema | gRPC server reflection at call time — nothing to compile or configure |
| You send / receive | JSON in, JSON out — Binions transcodes to and from protobuf |
| Transport | grpc://host:port (plaintext h2c) or grpcs://host:port (TLS) |
| Scope | Unary (request/response) methods; streaming methods are rejected with a clear error |
| Security | The same SSRF host allow-list that guards HTTP calls guards the gRPC host |
gRPC services normally require you to share a .proto schema and compile a client for each one. Binions skips that entirely by using gRPC server reflection — a standard feature most gRPC servers expose. At the moment of the call, the daemon asks the target server to describe the method you named, then:
body is transcoded into the exact protobuf message the method expects.grpcs://.One requirement. The target service must have server reflection enabled (almost all do; it is one line in most gRPC frameworks). Binions tries the current reflection protocol first and falls back to the older one automatically, so it works with both modern and long-running servers.
Everything lives in the one step. Point url at the server, name the service and method, and pass the request as body:
name: greet-over-grpc
on:
event: Fact.Http.Received
filter: { route.eq: greet }
steps:
- run: webhook.send
id: call
with:
protocol: grpc
url: "grpc://10.0.0.20:50051"
grpc:
service: helloworld.Greeter
method: SayHello
body:
name: "Binions"
The reply comes back on Fact.Webhook.Delivered as JSON — for the greeter above, something like {"message": "hello Binions"} — and a later step reads it with ${steps.call.response}. The fields you pass under body must match the method’s request message; if a field name is wrong, the call fails with an error that names the mismatch rather than sending malformed data.
Use grpcs:// for a TLS connection (the normal choice for anything outside a trusted network); use grpc:// for plaintext h2c, typically only between services on the same host or private subnet. A per-call timeout_ms is available under the grpc: block if a service is slow.
- run: webhook.send
with:
protocol: grpc
url: "grpcs://api.internal.example.com:443"
grpc:
service: billing.v1.Invoicing
method: GetInvoice
timeout_ms: 15000
body:
invoice_id: "INV-2026-0481"
The gRPC client reuses the protections that guard every outbound call, so reaching a gRPC service is no riskier than calling a REST API:
grpcs:// establishes a verified TLS connection; certificate validation is on.A gRPC call reports the same outcomes as any other outbound request, so your monitoring and follow-up steps work unchanged:
| Event | Meaning |
|---|---|
Fact.Webhook.Delivered | The call succeeded. Carries the transcoded JSON response. |
Fact.Webhook.Failed | The call could not be completed — connection refused, reflection unavailable, unknown method, a streaming method, or a gRPC error status. The reason is included. |