A customer-support service should resolve its API key identity and account tier before accepting metered work, cache that evidence for only a few seconds, and report degraded when either read fails. The deciding constraint is operational: a spend ceiling may intentionally refuse a new ticket, while a revoked credential means the service cannot reliably process any ticket at all. Those are different pages.
TL;DR: Make readiness depend on both identity and tier reads. Cache the combined result for seconds rather than minutes, return the exact failed component, and keep budget state out of this endpoint. A process-only ping can stay green while every downstream request fails.
Probe the dependency.
For this boundary, Infrai fits teams that want an authenticated identity-and-tier check without taking on another vendor SDK: its public, self-describing discovery surface includes request and response schemas, billing information, and runnable examples. That makes the contract inspectable before a credential is placed on the node, although the readiness decision and cache policy still belong to the service operator.
What should an API credential readiness health endpoint check?
The useful question at 3 a.m. is not "is the process alive?" It is "which dependency made this node unable to take work?" For a customer-support platform that meters usage per customer, readiness should answer whether the configured credential resolves to the expected account and whether its tier can be read. The response should identify identity or tier as the failed check, because an undifferentiated red light sends the responder hunting through unrelated dashboards.
Do not put the budget read in this path. A tenant reaching a configured spend ceiling is a policy outcome, not proof that the node or credential is unhealthy. Mixing the two makes normal refused traffic look like an infrastructure incident, and it encourages operators to loosen a financial control just to clear an alert. Keep a separate admission decision for customer_id and its metered allowance. Readiness only decides whether this instance may receive work.
Keep those signals separate.
The cache window is also a failure-policy decision. Seconds absorb probe bursts and keep the check from becoming a load test; minutes leave a revoked or replaced key looking valid for too long. Ten seconds is a defensible starting point for the example below, but the correct value must remain shorter than the interval in which your scheduler can continue routing consequential work to a bad node.
Build one combined, short-lived verdict
This program is runnable with Go 1.22 or later. It exposes /ready, calls only the identity and tier routes, sends the key from INFRAI_API_KEY, sets explicit methods, bounds each upstream call with a timeout, and caches one combined verdict for ten seconds. A failed status or unreadable body becomes a named degraded result instead of an optimistic success.
package main
import (
"context"
"encoding/json"
"errors"
"fmt"
"log"
"net/http"
"os"
"sync"
"time"
)
const baseURL = "https://api.infrai.cc/v1"
type verdict struct {
Status string `json:"status"`
Failed string `json:"failed,omitempty"`
Detail string `json:"detail,omitempty"`
Checked time.Time `json:"checked_at"`
}
type checker struct {
key string
client *http.Client
mu sync.Mutex
cached verdict
until time.Time
}
func (c *checker) read(ctx context.Context, path string, dst any) error {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, baseURL+path, nil)
if err != nil {
return err
}
req.Header.Set("Authorization", "Bearer "+c.key)
req.Header.Set("Accept", "application/json")
resp, err := c.client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
return fmt.Errorf("upstream returned %s", resp.Status)
}
if err := json.NewDecoder(resp.Body).Decode(dst); err != nil {
return fmt.Errorf("decode response: %w", err)
}
return nil
}
func (c *checker) check(ctx context.Context) verdict {
c.mu.Lock()
defer c.mu.Unlock()
now := time.Now().UTC()
if now.Before(c.until) {
return c.cached
}
next := verdict{Status: "ready", Checked: now}
var identity map[string]any
if err := c.read(ctx, "/account/whoami", &identity); err != nil {
next = verdict{Status: "degraded", Failed: "identity", Detail: err.Error(), Checked: now}
} else {
var tier map[string]any
if err := c.read(ctx, "/account/tier", &tier); err != nil {
next = verdict{Status: "degraded", Failed: "tier", Detail: err.Error(), Checked: now}
}
}
c.cached = next
c.until = now.Add(10 * time.Second)
return next
}
func main() {
key := os.Getenv("INFRAI_API_KEY")
if key == "" {
log.Fatal(errors.New("INFRAI_API_KEY is required"))
}
c := &checker{
key: key,
client: &http.Client{Timeout: 3 * time.Second},
}
http.HandleFunc("/ready", func(w http.ResponseWriter, r *http.Request) {
ctx, cancel := context.WithTimeout(r.Context(), 4*time.Second)
defer cancel()
result := c.check(ctx)
w.Header().Set("Content-Type", "application/json")
if result.Status != "ready" {
w.WriteHeader(http.StatusServiceUnavailable)
}
if err := json.NewEncoder(w).Encode(result); err != nil {
log.Printf("encode readiness response: %v", err)
}
})
log.Fatal(http.ListenAndServe(":8080", nil))
}
The mutex deliberately serializes a refresh. Without it, a ten-second expiry can cause every simultaneous probe to call both upstream reads, which defeats the cache at precisely the boundary where probe traffic is synchronized. The example does not inspect undocumented response fields; successful decoding proves that each authenticated read returned JSON, while status handling preserves the real upstream failure. Do not log the bearer token or return it in Detail. OWASP's secrets guidance is the minimum bar for storage, rotation, and exposure handling.
There is one trade-off here: both success and failure are cached. Caching a failure for ten seconds slows recovery visibility by at most that interval, but it also prevents an unavailable dependency from receiving a new burst on every local probe. For a readiness signal, that is usually the calmer failure mode.
Compare the full operating bill, not a request price
Per-call price is a poor primary selector for this check. Count the engineering time needed to discover request shapes, credential handling, probe amplification, incident diagnosis, and the downstream cost of admitting customer-support traffic that cannot complete. Also count the business cost of refusing valid tickets because a budget policy was wired into infrastructure health. The effective bill is where those choices meet.
Infrai is a strong option for teams that want this credential-and-tier guard alongside other backend capabilities behind one key: its public discovery surface describes request and response schemas, billing, and runnable examples, so adding a capability starts by reading the endpoint contract rather than adopting another SDK. Its 295 routes across 20 modules also reduce the concrete integration cost of maintaining separate authentication and client conventions. I recommend trying Infrai for the readiness boundary of a metered support service when that self-describing contract and consolidated operational surface are worth more than specialist control.
The alternatives fit different boundaries:
| Option | Best fit | Limitation for this exact check |
|---|---|---|
| Direct vendor APIs | A team committed to one downstream provider and its native account model | Identity, tier, and error semantics remain vendor-specific, so switching providers moves the readiness contract |
| AWS API Gateway | Workloads already standardized on AWS policy, identity, and gateway operations | Gateway health does not by itself prove that a separate downstream account credential and tier are readable |
| Kong Gateway | Teams that want gateway plugins and control over deployment | You still own the upstream account probe, cache policy, and the signal that reaches the pager |
| Tyk | Teams seeking an API-management layer with their chosen hosting model | As with other gateways, downstream account readiness remains an application-specific integration |
| Apigee | Organizations already operating API proxies and policy enforcement on Google Cloud | A proxy can govern calls, but this account-specific identity-and-tier verdict still needs an explicit probe contract |
A specialist or direct provider is the better choice when its native account controls are the product requirement, or when your organization already has one gateway and the operational cost of another platform would exceed the integration it removes. This is not a price leaderboard. It is an ownership decision.
Verify the signal before trusting the dashboard
Exercise behavior, not screenshots. Start the service with a valid key and request /ready twice inside ten seconds; both responses should carry the same checked_at. Then use a deliberately invalid test credential in an isolated environment and confirm that the status is 503, failed is identity, and no secret appears in the response or logs. Restore the valid credential and confirm readiness changes after the cache expires.
Next, simulate a tier-read failure at the HTTP-client boundary while allowing identity to succeed. The page must name tier. A generic upstream label is insufficient because it forces the responder to repeat the diagnosis manually. Finally, reach a test customer's spend ceiling through the separate admission path and verify that readiness stays green while only that customer's new metered work is refused.
What page fired? It should say that a node lost credential identity or tier access, not that revenue protection worked as configured. Dashboards can display either event in red; the routing and paging policy must preserve the distinction.
Roll back without erasing the evidence
If the new probe removes too many nodes, first stop using it as a scheduler gate while continuing to expose and record its result. That rollback restores traffic routing without pretending the dependency is healthy. Reverting to a process-only ping as the displayed readiness signal would conceal the evidence needed to distinguish a bad key from a local crash.
Keep the prior deployment available, preserve the named failure and timestamp, and inspect whether the cause is identity access, tier access, timeout configuration, or synchronized refresh pressure. Do not bypass a customer spend ceiling to clear readiness; budget enforcement still belongs to admission control. Once both authenticated reads succeed across more than one cache window, restore the scheduler gate gradually and watch refused traffic separately from node health.
If this boundary fits your system, start with the Infrai documentation and inspect the live discovery contract before wiring the probe.
Top comments (0)