Developer docs
The happy path: buy access, sign in, get an API key, and evaluate flags with traits. EdgeAssign is a precision tool: narrow scope, high signal, intentional design.
Quick navigation
Start in 60 seconds
- Buy access and activate your plan.
- Grab your API key from the account dashboard.
- Make your first check with
/v1/flag. - Add traits and move to
/v1/evalwhen needed.
Deterministic engine
EdgeAssign uses one deterministic mechanism for flags, rollouts, and bucketing. The same inputs always produce the same output.
// seed + flag + subject => stable bucket
bucket = hash(seed + flag + subject) % 100
eligible = rules(traits)
enabled = eligible && bucket < rollout_percent
One engine, one mental model. The differences are in the request shape, not the core mechanism.
Cookbook recipes
These are short recipes that change only the payload while the endpoint stays the same.
For the deeper architectural argument, see why local hashing breaks down in practice.
Feature flag check (simple)
GET /v1/flag/{project}/{flag}/{subject}
Percentage rollout
POST /v1/eval/{project}/{flag}
{
"subject": "alice"
}
User bucketing with a stable bucket
POST /v1/eval/{project}/{flag}
{
"subject": "alice"
}
// response includes bucket and rollout_percent
Trait-based gating
POST /v1/eval/{project}/{flag}
{
"subject": "alice",
"traits": {
"plan_tier": "pro",
"region_eu": true
}
}
EdgeAssign: Why not just hash locally?
At first glance, deterministic rollout seems trivial.
You can hash a user ID, apply a modulo, and decide whether a feature is enabled:
bucket = hash(user_id) % 100
enabled = bucket < rollout_percent
That works – and for simple cases, it is enough.
So why use EdgeAssign?
Because the hard part is not hashing. It is distributed seed management.
The real problem: distributed seed management
Local hashing requires every environment to hold the exact same master seed and hashing behavior.
If one client, service, or edge node runs an outdated seed or slightly different hashing behavior, the same user lands in different buckets across devices. That breaks rollout stability.
Warning: pushing a master seed to clients exposes cryptographic inputs. A reverse-engineered client can leak the seed, allowing precomputed or manipulated rollout outcomes. Centralizing the seed and logic in one API keeps cryptographic inputs off clients.
What goes wrong when you build it yourself
If you implement this locally, you now own:
Seed synchronization
Every environment must receive the exact same seed and update at the same time. Any drift splits users into different buckets across devices.
Client exposure risk
Shipping a master seed to browsers or mobile apps increases the blast radius if a client is reverse-engineered or compromised.
Cross-platform behavior drift
Differences in hashing libraries, byte encodings, or JSON parsing can change bucket results and silently destabilize rollouts.
Rule evolution
As rules become more complex, you need a way to define them, update them, and safely roll them out. This often turns into duplicated logic across repos and hard-to-debug edge cases.
What EdgeAssign does differently
EdgeAssign centralizes evaluation into a single API call. You send a subject (user or entity) and optional traits. EdgeAssign returns eligibility, rollout decision, and a stable bucket.
All derived deterministically:
bucket = hash(seed + flag + subject) % 100
enabled = eligible && bucket < rollout_percent
Because the decision is derived from stable inputs, it can be recomputed at any time.
That means no per-user assignment storage, no seed distribution to clients, and no drift between environments.
Deterministic evaluation also makes aggressive edge caching safe and scalable.
One source of truth
EdgeAssign acts as the single source of truth for rollout rule evaluation. Instead of reimplementing logic everywhere, you get consistent rule evaluation across all platforms, a single place to define and update logic, and predictable, reproducible results. The same input always produces the same output.
Trust, privacy, and compliance
Zero-retention privacy: EdgeAssign does not store per-user rollout assignments. Decisions are computed on demand and not retained as per-user records.
- Stateless allocation means no assignment table to audit, export, or purge.
- Pre-hash identifiers client-side if you want to avoid sending raw user data.
- Request bodies are not logged by default; exclude or scrub payloads in any external logging.
Privacy playbook
- Pre-hash client identifiers before sending requests.
- Evaluate deterministically through EdgeAssign.
- Log only safe outputs to Sentry, Datadog, or analytics.
SafeTrait pattern (recommended)
EdgeAssign centralizes deterministic rollout math and seed consistency. You still own business meaning, such as what qualifies as “pro” or “enterprise.”
Do local sanitization and send only non-PII traits. Map sensitive inputs into boolean or coarse labels before the payload leaves your infrastructure.
Boundary: EdgeAssign owns deterministic evaluation. You own sanitization before transmission. Centralize that mapping in one internal gateway to avoid cross-platform drift.
// Local sanitization example
const isEnterprise = email.endsWith("@megacorp.com");
const isEU = regionCode === "EU";
const traits = {
is_enterprise: isEnterprise,
region_eu: isEU,
plan_tier: "pro"
};
Before (raw):
{
"subject": "alice",
"traits": {
"email": "alice@megacorp.com",
"region": "EU",
"plan": "pro"
}
}
After (SafeTraits):
{
"subject": "alice",
"traits": {
"is_enterprise": true,
"region_eu": true,
"plan_tier": "pro"
}
}
Middleware sketch (Node/Edge):
// Example middleware to enforce SafeTraits
function buildSafeTraits(raw) {
return {
is_enterprise: raw.email?.endsWith("@megacorp.com") || false,
region_eu: raw.region === "EU",
plan_tier: raw.plan || "free"
};
}
Python example:
# Example sanitization helper
def build_safe_traits(raw):
return {
"is_enterprise": raw.get("email", "").endswith("@megacorp.com"),
"region_eu": raw.get("region") == "EU",
"plan_tier": raw.get("plan", "free")
}
Data boundary (simplified):
Client
-> Backend / gateway (sanitize & map to SafeTraits)
-> EdgeAssign API (deterministic eval)
-> Deterministic result
Recommended pattern: use a trait-sanitization gateway. Centralize trait mapping in one internal service so every client gets the same SafeTraits.
This architecture reduces the retention burden compared to traditional flag platforms that persist per-user assignments.
Privacy win: because assignments are derived rather than stored, there is no assignment-history retention layer to purge, migrate, or backfill.
Operational guarantees and boundaries
Guaranteed: deterministic output for the same inputs (project, flag, subject, traits) using the same configuration.
Not guaranteed: instant propagation under caching. Cache TTL governs how quickly rollout updates or key changes are observed.
Risks and tradeoffs
EdgeAssign is designed to be deterministic, focused, and cache-friendly, but there are practical tradeoffs to understand:
- Cached responses can delay rollout changes. Backend key rotation is immediate, but cached authorizations may linger until TTL expiry.
- If different systems send different traits for the same subject, results may differ across environments.
- If the API is unavailable, you need a fallback strategy that matches your risk tolerance.
- Rule support is intentionally narrower than a full experimentation platform.
- Pre-hashing identifiers improves privacy, but may reduce debugging visibility.
- API keys should be handled as secrets and excluded from logs, screenshots, and support requests.
For most teams, these tradeoffs are manageable and worth the simplicity, but they should be understood upfront.
Statelessness as a superpower
- Deterministic reproducibility for QA and incident response.
- Simpler disaster recovery with no assignment database to restore.
- No assignment replication across regions.
- Clean audit posture with fewer retained identifiers.
- Mathematical consistency of decisions for the same inputs.
Disaster recovery advantage
If a region goes down, a replacement region does not need assignment replication or state rehydration. Route traffic and continue with the same inputs and the same outputs.
QA and debugging
With deterministic decisions, you can reproduce a bug by replaying the same subject and traits. No assignment database clone required.
Production readiness
Designed for production rollout systems across multiple environments.
Latency expectations
Designed for request-path evaluation. Latency depends on your network and region; use caching for strict budgets and measure in your environment.
Statelessness implications
Because decisions are derived, not stored, there is no assignment table to migrate, backfill, or repair. You get reproducible decisions without state drift.
Summary
Hashing is easy. Distributed seed management is not.
EdgeAssign replaces distributed seeds, rule engines, assignment storage, and synchronization complexity with deterministic evaluation, a single source of truth, and reproducible decisions on demand.
Derived, not stored.
Auditing in a stateless world
Your systems already hold the authoritative inputs. EdgeAssign derives decisions from those inputs without becoming a second source of sensitive truth.
Log safe outputs like flag, enabled, reason, timestamp, and a request identifier. Avoid raw sensitive traits in logs.
Recommended fields: flag, enabled, reason, timestamp, request_id, user_id_hash, bucket. Avoid raw emails, IPs, device fingerprints, or private traits.
Opinionated integration pattern
Capture evaluation outputs during the request and forward them into your existing observability stack. This keeps EdgeAssign stateless while giving your team a practical support workflow.
At request time
Evaluate the flag, attach the result to the request context, and log it once.
In your logs
Index the result in Sentry, Datadog, or your analytics pipeline for later lookup.
// Pseudocode example (Node / Worker style)
const result = await edgeassign.evaluate({
flag: "pricing_experiment",
subject: userId,
traits: { plan: "pro" }
});
log.info("edgeassign_evaluation", {
flag: result.flag,
enabled: result.enabled,
reason: result.reason,
bucket: result.bucket,
timestamp: Date.now(),
request_id: requestId,
user_id_hash: hash(userId)
});
// Optional: attach to Sentry / Datadog
Sentry.setTag("edgeassign_flag", result.flag);
Sentry.setTag("edgeassign_enabled", String(result.enabled));
Sentry.setTag("edgeassign_reason", result.reason);
Datadog.log("edgeassign_evaluation", {
flag: result.flag,
enabled: result.enabled,
reason: result.reason,
bucket: result.bucket,
request_id: requestId
});
This pattern allows support to pull a request ID and immediately see what the user was served, without re-running buckets.
Production caching and fallback playbook
EdgeAssign is deterministic by design. That makes edge caching safe, fast, and globally scalable.
Recommended production default: cache derived evaluation responses at the edge for 30 seconds with up to 60 seconds of stale-while-revalidate. Client/browser caching should be off, or very conservative, for evaluation responses.
If a cached evaluation is still being served, changing the rollout does not change the response until that cache expires or is invalidated. Deterministic evaluation plus caching equals stability; cache invalidation controls how quickly updates propagate.
Without cache invalidation, users may continue receiving previously cached evaluation results until the TTL and stale window expire.
Origin limits: published rate limits apply at the origin. With edge caching, most traffic never reaches origin, so origin limits rarely reflect end-user volume.
Normal caching
Cache derived evaluations at the edge for 30 seconds to keep updates fast and predictable.
Stale-while-revalidate
Serve stale results for up to 60 seconds while refreshing in the background to avoid traffic spikes.
Fallback behavior
If the edge cache is down, fall back to direct evaluation; if evaluation fails, fall back to a safe default outcome.
Purge guidance
For urgent rollbacks, set rollout to 0% and purge/invalidate the edge cache immediately.
Rollback flow
Set rollout to 0%
-> Invalidate edge cache
-> Fresh evaluation served
Request flow (simplified):
Client request
-> Edge cache (hit? serve cached result)
-> Cache miss? evaluate at origin
-> Store response in cache
-> Return deterministic result
Operational note: config changes should be treated as instant at origin, but not instant at every cache layer unless purged.
Incident-response checklist
Use this exact sequence for emergency rollbacks or critical bugs in production.
- Set the rollout to 0% (or route to a safe control outcome).
- Purge or invalidate the cache for the affected flag.
- Verify fresh evaluations are being served (spot-check with a known test key).
- Confirm propagation by checking new logs for the updated enabled state.
- Document the incident and restore normal rollout when safe.
Request path (simplified)
Client request
-> EdgeAssign evaluate (deterministic)
-> Cache hit? serve cached result
-> Cache miss? compute + cache
-> Log safe outputs
-> Observability (Sentry / Datadog / analytics)
If documentation does not resolve the issue, contact support with the request details.
1. Buy access
Choose a plan on the homepage and complete checkout.
2. Sign in
Use the account page to sign in, reset your password, and manage billing.
3. Get your API key
Your API key is shown on the account dashboard once your subscription is active.
API base URL: https://api.edge-assign.com
4. Check a flag (simple)
# PowerShell
curl.exe -H "X-API-Key: YOUR_API_KEY" "https://api.edge-assign.com/v1/flag/acme-prod/new_checkout/alice"
# macOS / Linux
curl -H "X-API-Key: YOUR_API_KEY" \
"https://api.edge-assign.com/v1/flag/acme-prod/new_checkout/alice"
When to use each endpoint
- /v1/flag: simple deterministic rollout checks.
- /v1/eval: when traits/rules matter.
- /v1/eval-batch: many evaluations in one call.
5. Evaluate with traits (rules + rollout)
Warning: do not send raw PII in traits. Use SafeTraits (local mapping to booleans or coarse labels) before data leaves your infrastructure.
# PowerShell (safe JSON)
$body = @'
{"subject":"alice","traits":{"beta":true,"plan":"pro"}}
'@
curl.exe -X POST -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" --data-binary $body "https://api.edge-assign.com/v1/eval/acme-prod/new_checkout"
# macOS / Linux
curl -X POST -H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"subject":"alice","traits":{"beta":true,"plan":"pro"}}' \
"https://api.edge-assign.com/v1/eval/acme-prod/new_checkout"
Example response:
{
"project": "acme-prod",
"flag": "new_checkout",
"subject": "alice",
"eligible": true,
"enabled": true,
"bucket": 8,
"rollout_percent": 25,
"reason": "eligible_and_in_rollout"
}
Batch evaluate subjects
# PowerShell (safe JSON)
$body = @'
{"items":[{"subject":"alice","traits":{"plan":"pro"}},{"subject":"bob","traits":{"plan":"free"}}]}
'@
curl.exe -X POST -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" --data-binary $body "https://api.edge-assign.com/v1/eval-batch/acme-prod/new_checkout"
# macOS / Linux
curl -X POST -H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"items":[{"subject":"alice","traits":{"plan":"pro"}},{"subject":"bob","traits":{"plan":"free"}}]}' \
"https://api.edge-assign.com/v1/eval-batch/acme-prod/new_checkout"
Request schema
Eval request:
{
"subject": "string (required)",
"traits": {
"key": "string | number | boolean | null"
}
}
Eval-batch request:
{
"items": [
{
"subject": "string (required)",
"traits": { "key": "string | number | boolean | null" }
}
]
}
Admin flags request (rules optional):
{
"project": "string (required)",
"flag": "string (required)",
"rollout_percent": "number 0-100 (required)",
"rules": [
{ "field": "string", "op": "eq|in|exists", "value": "any (optional)" }
]
}
Trait values should be consistent across systems for the same subject. Use SafeTraits and avoid raw PII in trait fields.
6. Check usage
Usage is available in the account dashboard.
Usage limits and grace
- Warnings at 80%, 90%, and 100% of plan limit.
- At 100%, a 7-day grace window starts (up to 120%).
- After grace, requests are throttled (slower), not hard-stopped.
- Billing never changes without explicit opt-in.
Limits and behavior summary
| Category | Summary |
|---|---|
| Rate limits | 600 requests/min per API key, 1,200 requests/min per IP. Batch counts per item. |
| Usage grace | Warnings at 80/90/100%. 7-day grace up to 120% after limit is reached; throttling after grace. |
| Inactive subscription | API keys are hidden; API requests return 401. |
| Caching | Deterministic responses are cacheable; TTL affects update propagation. |
Upgrade path
Pro is opt-in only. We never change billing unless a customer explicitly consents.
7. Manage billing
Use the account dashboard to open the Stripe billing portal and cancel or update your payment method.
Flag management (API)
Update rollouts programmatically as part of CI/CD or internal tooling.
# PowerShell (safe JSON)
$body = @'
{"project":"acme-prod","flag":"new_checkout","rollout_percent":25}
'@
curl.exe -X POST "https://api.edge-assign.com/admin/flags" -H "X-Admin-Token: YOUR_ADMIN_TOKEN" -H "Content-Type: application/json" --data-binary $body
# macOS / Linux
curl -X POST "https://api.edge-assign.com/admin/flags" \
-H "X-Admin-Token: YOUR_ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"project":"acme-prod","flag":"new_checkout","rollout_percent":25}'
Example: bump a rollout from 10% to 25% by updating rollout_percent.
Flag rules example
# PowerShell (safe JSON)
$body = @'
{
"project": "acme-prod",
"flag": "new_checkout",
"rollout_percent": 25,
"rules": [
{ "field": "plan", "op": "in", "value": ["pro","team"] },
{ "field": "beta", "op": "eq", "value": true }
]
}
'@
curl.exe -X POST "https://api.edge-assign.com/admin/flags" -H "X-Admin-Token: YOUR_ADMIN_TOKEN" -H "Content-Type: application/json" --data-binary $body
Rules support eq, in, and exists.
How deterministic bucketing works
Each project has a seed. We hash seed + subject for the bucket endpoint, and seed + flag + subject for flag evaluation. The same inputs return the same bucket every time.
What is stored (and what is not)
We store project config, flags, and usage counters. We do not store per-user rollout assignments.
Rule engine support
Rules are evaluated before rollout. Supported ops: eq, in, exists (including exists: false).
Security notes
- API keys are secrets.
- Do not embed keys in public client apps.
- Do not email keys or paste them into support threads.
- Exclude keys and sensitive payloads from logs where needed.
Key rotation
Rotating a key invalidates the old key immediately. Cached authorizations may still allow requests until TTL expiry, so update your services as soon as the new key is issued.
Rate limits
Default limits: 600 requests/min per API key and 1,200 requests/min per IP. Batch requests count per item.
Response codes and common errors
Typical responses (exact status depends on endpoint and validation):
- 200: success
- 400: malformed request or invalid input
- 401: invalid or inactive API key
- 404: missing project/flag (when applicable)
- 429: rate limit exceeded
- 5xx: service error
Subscription inactive behavior
When a subscription is inactive or past its paid period, API keys are hidden and API requests return 401.
Performance notes
EdgeAssign is designed for low-latency evaluation. If the call sits on a critical path, cache results or evaluate once per request lifecycle.
Common integration mistakes
- Inconsistent subject IDs across systems.
- Inconsistent trait shapes or types.
- Caching too aggressively for fast-moving rollouts.
- Forgetting to update rotated keys.
- Using API keys in unsafe client-side contexts.
