Conflicting webhook events. Race conditions. Stale state. One API call returns a single authoritative answer โ in 47ms. No polling. No state machines. No guessing.
Used for: IoT fleet management · webhook deduplication · edge device sync · real-time sensor arbitration · distributed state reconciliation
This is your actual production endpoint. Not a sandbox. Not a mock. Edit the payload below and see real resolution happen right now. For security purposes all payload input will be wiped from our server once you leave this page.
"api_key": "demo" unchanged. You can freely edit state device names, timestamps, and values to test your own data.
Your dashboard says online. The job runs. It fails. 23% of your alerts are false positives and your SLA is bleeding out.
By the time your polling cycle catches the dropout, the customer already noticed. Late alerts don't save SLAs. They document failures.
Weak WiFi, LTE dropout, duplicate events. Your system is making decisions on corrupted input and calling it telemetry.
Conflicting timestamps, duplicate events, and weak signals get resolved into one authoritative state. Every time. Automatically.
POST your events. Get back ground truth. No polling architecture to maintain. No custom state machine to debug at 2am.
Idempotent by design. Send the same event twice โ get the same answer once. Ordered processing guaranteed. Zero duplicate side effects.
Your resolution layer can't go down when your devices can't either. Offline-safe, always ready, built for infrastructure that never sleeps.
Start resolving device state in under 2 minutes. Enter your email โ get your API key and 1,000 free resolutions instantly. No credit card required.
Join engineers already resolving millions of device states per month.
1,000 resolutions
1 API key
Perfect for local testing and proof of concept.
10,000 resolutions
1 API Key
For solo developers running live projects.
100,000 resolutions
1 API key
Priority support
For teams shipping to production with real devices.
100,000+ resolutions
SLA guarantees
Need burst capacity for an event? Let's talk.
1M+ resolutions
ISO 27001, HIPAA, PCI DSS, FISMA
Exceeds industry specific compliance
SLA guarantees, custom SDKs & models, dedicated support.
*Typical enterprise deployments range between $5K-$10K per month based on fleet size and SLA requirements*
Sign + Resolve โ Live API
โ LIVE pip install signalcend
โ LIVE npm i signalcend
new SignalCend(apiKey, secret).resolve(events)
Complete /v1/resolve spec
Enter your email above โ You'll receive an api_key and a unique api_secret instantly. Your api_secret is shown once โ copy it now. It has also been sent to your email.
api_secret to a repository or expose it in client-side code.
SignalCend detected a race condition โ the offline event arrived late, already superseded by a confirmed reconnect. Every decision is explained in the response.
https://www.signalcend.com/v1
/sign, /resolve, /usage) remain active for backward compatibility. All new integrations should use the /v1/ prefix.
SignalCend uses per-customer HMAC-SHA256 request signing. Every customer receives a unique api_key and api_secret at signup. Your api_secret is shown once โ at signup and in your welcome email. Store it securely.
Every request to /v1/resolve must include an X-Signature header containing the HMAC-SHA256 hex digest of your raw request body, signed with your api_secret. Generate this signature by POSTing your payload to /v1/sign first.
api_secret in client-side code, public repositories, or logs. It cannot be retrieved after your welcome email. Contact support to rotate your secret if it is compromised.
SignalCend uses a deterministic multi-signal arbitration algorithm. The decision tree executes in this exact order on every /v1/resolve call:
The full arbitration trace is returned in every response. Every decision made in steps 1โ5 is reflected in arbitration_method, arbitration_signals_used, conflicts_detected, and resolution_basis. Nothing is implicit.
Rate limits apply to all plans including trial. Limits are designed to catch runaway integrations, not constrain legitimate usage.
| Window | Limit | Error Code | HTTP |
|---|---|---|---|
| Per second | 50 requests | RATE_LIMITED |
429 |
| Per minute | 1,000 requests | RATE_LIMITED |
429 |
| Per hour | 10,000 requests | RATE_LIMITED |
429 |
Enterprise plans with higher rate limit requirements should contact support. Every /v1/resolve response now includes rate limit headers: X-RateLimit-Limit (requests allowed per window), X-RateLimit-Remaining (requests left in current window), X-RateLimit-Reset (Unix timestamp when the window resets), and X-RateLimit-Window (window size in seconds). These are returned on all resolve responses including rate-limited and cached responses.
The /v1/resolve endpoint is strictly idempotent. Identical payloads always produce identical responses regardless of how many times they are submitted. This guarantee holds for 30 days from the original resolution.
Idempotency is determined by a SHA-256 hash of the complete sorted payload. The resulting resolution_id is deterministic โ you can predict it before making a call if you know the payload. Every response includes an idempotency_expires_at timestamp showing exactly when the guarantee expires for that resolution.
Duplicate submissions within the 30-day window return "status": "already_processed" with the original resolved_state and are never billed. After 30 days the same payload is treated as a new resolution and billed normally.
Every resolution returns a confidence float between 0.20 and 1.0, and a recommended_action enum โ ACT (โฅ0.85), CONFIRM (0.65โ0.84), LOG_ONLY (0.50โ0.64), or LOG_ONLY (0.20โ0.49, severe degradation) โ so your integration can branch on a named signal rather than hardcoding thresholds against the raw float.
| Score | Meaning | Recommended Action |
|---|---|---|
0.95 โ 1.00 |
High โ clean signal, no conflicts | Act immediately. Trigger automations, update state, alert downstream systems. |
0.80 โ 0.94 |
Good โ minor signal issues resolved | Act with normal confidence. Review conflicts_detected for context. |
0.65 โ 0.79 |
Moderate โ clock drift or weak signal present | Consider a brief confirmation window before triggering critical automations. |
0.50 โ 0.64 |
Low โ multiple signal issues, best available resolution | Log for audit. Confirm via secondary signal before acting on critical state changes. |
0.20 โ 0.49 |
Severe โ maximum degradation across multiple signals | Log only. Do not act without manual review or secondary signal confirmation. |
The floor of 0.20 is intentional. It signals that even under maximum uncertainty, SignalCend has applied every available arbitration signal and returned the best possible answer rather than an error. A result is always more useful than silence. Scores between 0.20 and 0.49 indicate severe multi-signal degradation โ log for audit and do not act without manual review or a secondary signal confirmation.
Trial accounts receive 1,000 free resolutions. Trial resolutions never expire as long as you are actively building. If your API key goes 7 days without a call, your trial is paused and your remaining resolutions are preserved. Upgrade at any time to reactivate โ same key, same endpoint, no code changes required.
If a call is made on a paused trial account, the API returns TRIAL_PAUSED_INACTIVITY with your remaining resolution count and a reactivation link.
/v1/sign
Generate request signature
Accepts your raw JSON payload and returns an HMAC-SHA256 signature using your per-customer api_secret. Pass the returned signature in the X-Signature header of your /v1/resolve request. The demo key uses a shared public secret โ production keys use your unique secret. Add ?format=canonical (or format:canonical in the request body) for deterministic key-sorted signing โ the response will confirm with canonical: true. Use canonical mode when your client cannot guarantee consistent key ordering.
/v1/sign must be byte-for-byte identical to the body sent to /v1/resolve. Any difference โ including whitespace or key ordering โ will produce a signature mismatch and a 403 response.
/v1/sign must always be called over HTTPS. Never call this endpoint over HTTP โ your api_key is transmitted in the request body. On network failure, retry with the identical payload bytes. Do not reserialize the payload before retrying.
/v1/resolve
Resolve device state
Accepts a signed device state payload. Runs the full multi-signal arbitration algorithm and returns a single authoritative resolved state with confidence scoring, arbitration trace, and conflict analysis. Strictly idempotent for 30 days.
| Header | Required | Value |
|---|---|---|
Content-Type |
Yes | application/json |
X-Signature |
Yes | HMAC-SHA256 hex digest from /v1/sign |
Single device event. Supports the full arbitration algorithm including race condition detection, RF signal analysis, sequence number tracking, and clock drift compensation.
| Field | Type | Required | Description |
|---|---|---|---|
api_key |
string | Yes | Your API key. Use "demo" for testing. |
state.device_id |
string | Yes | Unique device identifier. |
state.status |
string | Yes | Reported device status. Accepted values: online, offline, idle, error, warning, updating, initializing and common aliases. |
state.timestamp |
string | Yes | ISO 8601 event timestamp. e.g. 2026-01-15T14:32:04Z. Used as primary arbitration signal when within drift threshold. |
state.signal_strength |
number | No | RF signal strength in dBm. Enables RF noise analysis. Aliases: rssi, snr. |
state.sequence |
integer | No | Monotonic sequence number. A reset to 0 triggers device restart detection. Aliases: seq, sequence_number. |
state.reconnect_window_seconds |
integer | No | Race condition detection window in seconds. Default: 30. Maximum: 600. Set higher for hardware with slow reconnect cycles (satellite uplinks, deep-sleep devices). The applied value is returned in reconnect_window_seconds. |
state.value |
any | No | Sensor reading or measurement. Passed through to resolved_state.sensor_value. |
state.battery |
number | No | Battery level. Passed through to resolved_state.battery_level. |
state.firmware |
string | No | Firmware version string. Passed through to resolved_state.firmware_version. |
state.location |
string | No | Device location or zone identifier. Passed through to resolved_state.location. |
state.lat / state.lon |
number | No | GPS coordinates. Returned as resolved_state.coordinates: {"lat": ..., "lon": ...} when both are present. |
state.temperature |
number | No | Temperature reading. Passed through. Alias: temp. |
state.humidity |
number | No | Humidity reading. Passed through. |
state.pressure |
number | No | Pressure reading. Passed through. |
Multi-device batch format. Submit multiple events per device for timestamp-based arbitration across event sets. Confidence scoring and drift detection apply per device.
| Field | Type | Required | Description |
|---|---|---|---|
api_key |
string | Yes | Your API key. |
events |
object | Yes | Map of device_id โ array of event objects. Each event object requires timestamp and value. Optional per-event: signal_strength. |
| Field | Type | Always Present | Description |
|---|---|---|---|
status |
string | Yes | success or already_processed |
resolution_id |
string | Yes | Deterministic ID derived from payload hash. Identical payloads always produce the same ID. |
billed |
boolean | Yes | false on trial accounts and duplicate submissions. true on fresh paid resolutions. |
idempotency_expires_at |
string | Yes | ISO 8601 timestamp. After this point the same payload is treated as a new resolution. |
trial_remaining |
integer | Trial only | Free resolutions remaining on trial accounts. |
resolved_state.device_id |
string | Yes | Device identifier echoed from request. |
resolved_state.authoritative_status |
string | Yes | The single resolved truth after full arbitration. One of: online, offline, idle, error, warning, updating, initializing. |
resolved_state.confidence |
float | Yes | Arbitration confidence score 0.20โ1.0. See Confidence Score Interpretation above. |
resolved_state.recommended_action |
string | Yes | Action enum derived from confidence score. ACT (โฅ0.85) โ act autonomously. CONFIRM (0.65โ0.84) โ confirm before acting. LOG_ONLY (0.50โ0.64) โ log and monitor only. LOG_ONLY (0.20โ0.49) โ severe degradation, do not act without manual review. |
resolved_state.arbitration_method |
string | Yes | direct_resolution ยท race_condition_resolution ยท multi_signal_arbitration ยท noise_filtered_resolution ยท drift_compensated_resolution |
resolved_state.arbitration_signals_used |
array | Yes | Ordered list of signals applied during arbitration. e.g. ["event_arrival_time", "device_timestamp", "rf_signal_quality", "reconnect_supersession"] |
resolved_state.deduplication_fingerprint |
string | Yes | 16-char SHA-256 derived payload fingerprint. Use to verify idempotency in your own audit logs. |
resolved_state.race_condition_resolved |
boolean | Yes | true when a late-arriving disconnect event was detected within the reconnect window and overridden by confirmed reconnect. |
resolved_state.clock_drift_compensated |
boolean | Yes | true when device timestamp was outside drift threshold and server-side arrival sequencing was applied instead. |
resolved_state.reconnect_window_seconds |
integer | Yes | The reconnect window applied during this resolution. Confirms what value was used when caller overrides the default. |
resolved_state.resolution_basis |
object | Yes | Contains timestamp_confidence (high/medium/low), signal_quality (strong/moderate/weak/critical), conflicts_resolved (integer count). |
resolved_state.conflicts_detected |
array | When present | Human-readable description of each conflict detected and how it was resolved. Omitted when no conflicts exist. |
resolved_state.event_timestamp |
string | When parseable | Parsed ISO 8601 device timestamp. Omitted when timestamp was unparseable. |
resolved_state.signal_strength_dbm |
number | When submitted | Signal strength echoed from request. |
resolved_state.signal_note |
string | Weak/critical only | Human-readable signal quality note. Only present when signal is weak or critical. |
resolved_state.firmware_version |
string | When submitted | Echoed from request. |
resolved_state.battery_level |
number | When submitted | Echoed from request. |
resolved_state.coordinates |
object | When submitted | {"lat": ..., "lon": ...} โ present only when both lat and lon submitted. |
| Field | Type | Description |
|---|---|---|
resolved_state.[device_id].authoritative_value |
any | Single authoritative value after timestamp arbitration across all submitted events. |
resolved_state.[device_id].confidence |
float | Per-device confidence score 0.20โ1.0. |
resolved_state.[device_id].arbitration_method |
string | timestamp_arbitration or drift_compensated_resolution |
resolved_state.[device_id].deduplication_fingerprint |
string | 16-char idempotency fingerprint for this device's event set. |
resolved_state.[device_id].clock_drift_suspected |
boolean | true when timestamp spread across submitted events exceeded 1 hour. |
resolved_state.[device_id].events_evaluated |
integer | Number of valid events evaluated before reaching the authoritative value. |
| error_code | HTTP | Meaning | Resolution |
|---|---|---|---|
INVALID_SIGNATURE |
403 | X-Signature missing or incorrect | Ensure the body sent to /v1/sign is byte-for-byte identical to the body sent to /v1/resolve. Add header X-Signature-Debug: 1 to receive a signature_debug object in the response with the received body hash, expected signature, and a plain-language hint. |
invalid_api_key |
403 | API key not found | Verify your api_key value. Keys are case-sensitive UUIDs. |
account_inactive |
403 | Account suspended | Check your email for next steps or contact support. |
TRIAL_PAUSED_INACTIVITY |
403 | Trial paused after 7 days without a call | Remaining resolutions preserved. Upgrade to reactivate โ same key, same endpoint, no code changes. |
EMPTY_STATE |
400 | State payload is empty or missing | Provide at minimum: device_id, status, timestamp. |
MISSING_FIELDS |
400 | Required fields missing | Response includes a required_fields array listing exactly what is missing. |
MISSING_EVENTS |
400 | Structured format missing events key | Add an events key containing your device event map. |
INVALID_JSON |
400 | Request body is not valid JSON | Validate JSON before sending. Ensure Content-Type is application/json. |
RATE_LIMITED |
429 | Rate limit exceeded | Back off and retry. Response message specifies which limit was hit. Enterprise plans available for higher limits. |
already_processed |
200 | Duplicate payload within idempotency window | Original resolved_state returned. Not billed. This is expected behavior for retry logic. |
replay_contextPresent on every flat resolution response. Provides the policy version, ruleset, resolution class, and degradation flags required for independent audit replay without depending on SignalCend internal state.
| Field | Type | Description |
|---|---|---|
policy_version |
string | Version of the arbitration policy applied to this resolution. |
ruleset_id |
string | Fully qualified ruleset identifier. |
resolution_class |
string | deterministic if all inputs were clean. confidence_weighted if signal degradation flags are present. |
resolution_inputs_hash |
string | SHA-256 hash of the resolution inputs. Same inputs always produce the same hash. |
signal_degradation_flags |
array | Degradation conditions detected. Empty array means clean inputs. Possible values: clock_drift, weak_rf_signal, sequence_reset, brief_dropout, reconnect_window_override_blocked. |
resolution_timestamp_utc |
string | ISO 8601 timestamp of when the resolution was computed server-side. |
resolution_mode |
string | live โ all events arrived within 60 seconds of resolution time. replay โ one or more events are older than 60 seconds, indicating an offline catch-up batch. Branch on this field to differentiate live state from historical reconstruction. |
event_age_seconds |
integer | Age in seconds of the oldest event in the batch relative to resolution_timestamp_utc. Use this to determine how stale the resolved state is. A value of 0โ60 indicates live data. Values above 3600 indicate the device was offline for over an hour. |
recommended_actionAn explicit enum derived from the confidence score. Use this to branch your application logic rather than implementing your own threshold mapping.
| Value | Confidence | Meaning |
|---|---|---|
ACT |
≥ 0.85 | High confidence. Safe to trigger automations. |
CONFIRM |
0.65 – 0.84 | Qualified resolution. Verify via secondary signal before acting. In safety-critical systems this may mean querying a secondary sensor. In a fleet dashboard this may mean displaying a qualified state indicator and waiting for the next heartbeat. |
LOG_ONLY |
< 0.65 | Low confidence. Log and monitor. Do not trigger automations. A probabilistic state should never automatically trigger an irreversible actuation. |
session_id optionalA caller-supplied session identifier that enables stateful arbitration across sequential calls for the same device. Passed at the top level of the payload alongside api_key, not inside the state object. Session context is stored for 300 seconds from the last reconnect event.
Without a session_id each call is evaluated independently. With a session_id the engine compares the incoming sequence number against the last stored reconnect sequence. If the offline event carries a higher sequence number, sequence authority wins and the reconnect window override is blocked.
The API accepts both flat single-device payloads and structured multi-device batch payloads. Use the events key with device IDs as top-level keys, each containing an array of timestamped events.
| Criteria | Use Flat /resolve |
Use Batch /resolve |
|---|---|---|
| Time-to-consistency requirement | Latency budget <100ms โ critical control loops, emergency actions | Tolerance >200ms โ telemetry, state sync, analytics |
| Error isolation | Required โ retry individual events without affecting others | Not required โ batch-level retry is acceptable |
| Device count | 1โ2 devices, low volume (<5 events/min) | 3โ100 devices, or events share a correlation context |
| Offline catch-up / replay | Not recommended โ high call volume for large replay windows | Preferred โ send all replay events in chunked batches with shared session_id |
| Network cost | Higher โ one HTTP round trip per device | Lower โ mobile and low-power devices benefit from fewer round trips |
{
"api_key": "demo",
"events": {
"sensor_007": [
{"timestamp": "2026-01-15T14:32:01Z", "value": "offline", "signal_strength": -82},
{"timestamp": "2026-01-15T14:32:03Z", "value": "online", "signal_strength": -71}
],
"sensor_012": [
{"timestamp": "2026-01-15T14:32:00Z", "value": "idle"}
]
}
}
Note: batch resolutions return authoritative_value per device rather than authoritative_status. The replay_context, recommended_action, and conflicts_detected fields are present on both flat and batch resolutions.
400 PAYLOAD_TOO_LARGE. For reconnect storms involving more than 100 devices, chunk requests by device group and pass the same session_id across chunks to preserve reconnect window arbitration continuity.
When sequential events include sequence numbers, the engine evaluates continuity between consecutive values and classifies each transition as one of three patterns. Understanding this classification boundary is essential when constructing test harnesses or implementing sequence-based ordering logic.
| Pattern | Condition | Signal flag | Confidence penalty | Meaning |
|---|---|---|---|---|
| Normal progression | seq[n] > seq[n-1] | none | none | Clean sequential ordering. No action required. |
| Causal inversion | seq[n] < seq[n-1] and delta < 100 | sequence_inversion |
-0.08 | Late-arriving event. Causal ordering is ambiguous. Engine applies arrival-sequence fallback for this transition. |
| Sequence reset | seq[n] < seq[n-1] and delta ≥ 100, or seq[n] = 0 | sequence_reset |
-0.05 | Device restart pattern detected. Sequence continuity restored from zero. Lower penalty than inversion โ restart is an expected event. |
When constructing test payloads that target the inversion path specifically, keep consecutive sequence deltas below 100. Programmatically generated sequences with large gaps will be classified as resets regardless of intent, applying a different penalty weight and producing different arbitration_method labels.
Every resolution includes fields that disclose how events were ordered and whether that ordering is safe to act on in your environment.
| Field | Type | Description |
|---|---|---|
resolution_authority |
string | The signal that determined the final resolution. Values: sequence_number, rf_signal, device_timestamp, clock_drift_compensation, reconnect_window, majority_vote, single_event. |
ordering_mechanism |
string | device_timestamp โ event order determined by device-reported timestamp. server_arrival_sequence โ clock drift detected, arrival order used as fallback. |
ordering_trust |
string | high โ device timestamps used and trustworthy. conditional โ arrival sequencing used due to clock drift; trust depends on broker topology. |
transport_warning |
string | null | Present only when ordering_mechanism is server_arrival_sequence. In environments with asymmetric broker paths or partitioned consumers (MQTT, Kafka), arrival order may not reflect true event order. Gate automated actions on CONFIRM or higher when this field is present. |
/v1/usage
Get account usage
Returns real-time usage data including trial balance, monthly quota, and a 30-day daily breakdown.
| Parameter | Required | Description |
|---|---|---|
api_key |
Yes | Your API key. |
| Field | Type | Description |
|---|---|---|
account_status |
string | active, past_due, or suspended |
trial_remaining |
integer | Free trial resolutions remaining. |
monthly_quota |
integer | Total resolutions allowed per month on your current plan. |
monthly_used |
integer | Resolutions used this billing period. |
daily_usage |
object | {"YYYY-MM-DD": count} โ last 30 days. Only days with at least one resolution are included. |
total_last_30_days |
integer | Sum of all resolutions across the last 30 days. |
| Version | Date | Changes |
|---|---|---|
v1 |
2026-02-26 | Initial versioned release. Per-customer HMAC secrets. Configurable reconnect window. Full arbitration trace in every response. Confidence scoring. Idempotency guarantee with expiry timestamp. Production rate limiting. Activity-based trial expiry. |
https://www.signalcend.com/v1
/sign, /resolve, /usage) remain active for backward compatibility. All new integrations should use the /v1/ prefix.
SignalCend uses per-customer HMAC-SHA256 request signing. Every customer receives a unique api_key and api_secret at signup. Your api_secret is shown once โ at signup and in your welcome email. Store it securely.
Every request to /v1/resolve must include an X-Signature header containing the HMAC-SHA256 hex digest of your raw request body, signed with your api_secret. Generate this signature by POSTing your payload to /v1/sign first.
api_secret in client-side code, public repositories, or logs. It cannot be retrieved after your welcome email. Contact support to rotate your secret if it is compromised.
SignalCend uses a deterministic multi-signal arbitration algorithm. The decision tree executes in this exact order on every /v1/resolve call:
The full arbitration trace is returned in every response. Every decision made in steps 1โ5 is reflected in arbitration_method, arbitration_signals_used, conflicts_detected, and resolution_basis. Nothing is implicit.
Rate limits apply to all plans including trial. Limits are designed to catch runaway integrations, not constrain legitimate usage.
| Window | Limit | Error Code | HTTP |
|---|---|---|---|
| Per second | 50 requests | RATE_LIMITED |
429 |
| Per minute | 1,000 requests | RATE_LIMITED |
429 |
| Per hour | 10,000 requests | RATE_LIMITED |
429 |
Enterprise plans with higher rate limit requirements should contact support. Every /v1/resolve response now includes rate limit headers: X-RateLimit-Limit (requests allowed per window), X-RateLimit-Remaining (requests left in current window), X-RateLimit-Reset (Unix timestamp when the window resets), and X-RateLimit-Window (window size in seconds). These are returned on all resolve responses including rate-limited and cached responses.
The /v1/resolve endpoint is strictly idempotent. Identical payloads always produce identical responses regardless of how many times they are submitted. This guarantee holds for 30 days from the original resolution.
Idempotency is determined by a SHA-256 hash of the complete sorted payload. The resulting resolution_id is deterministic โ you can predict it before making a call if you know the payload. Every response includes an idempotency_expires_at timestamp showing exactly when the guarantee expires for that resolution.
Duplicate submissions within the 30-day window return "status": "already_processed" with the original resolved_state and are never billed. After 30 days the same payload is treated as a new resolution and billed normally.
Every resolution returns a confidence float between 0.20 and 1.0, and a recommended_action enum โ ACT (โฅ0.85), CONFIRM (0.65โ0.84), LOG_ONLY (0.50โ0.64), or LOG_ONLY (0.20โ0.49, severe degradation) โ so your integration can branch on a named signal rather than hardcoding thresholds against the raw float.
| Score | Meaning | Recommended Action |
|---|---|---|
0.95 โ 1.00 |
High โ clean signal, no conflicts | Act immediately. Trigger automations, update state, alert downstream systems. |
0.80 โ 0.94 |
Good โ minor signal issues resolved | Act with normal confidence. Review conflicts_detected for context. |
0.65 โ 0.79 |
Moderate โ clock drift or weak signal present | Consider a brief confirmation window before triggering critical automations. |
0.50 โ 0.64 |
Low โ multiple signal issues, best available resolution | Log for audit. Confirm via secondary signal before acting on critical state changes. |
0.20 โ 0.49 |
Severe โ maximum degradation across multiple signals | Log only. Do not act without manual review or secondary signal confirmation. |
The floor of 0.20 is intentional. It signals that even under maximum uncertainty, SignalCend has applied every available arbitration signal and returned the best possible answer rather than an error. A result is always more useful than silence. Scores between 0.20 and 0.49 indicate severe multi-signal degradation โ log for audit and do not act without manual review or a secondary signal confirmation.
Trial accounts receive 1,000 free resolutions. Trial resolutions never expire as long as you are actively building. If your API key goes 7 days without a call, your trial is paused and your remaining resolutions are preserved. Upgrade at any time to reactivate โ same key, same endpoint, no code changes required.
If a call is made on a paused trial account, the API returns TRIAL_PAUSED_INACTIVITY with your remaining resolution count and a reactivation link.
/v1/sign
Generate request signature
Accepts your raw JSON payload and returns an HMAC-SHA256 signature using your per-customer api_secret. Pass the returned signature in the X-Signature header of your /v1/resolve request. The demo key uses a shared public secret โ production keys use your unique secret. Add ?format=canonical (or format:canonical in the request body) for deterministic key-sorted signing โ the response will confirm with canonical: true. Use canonical mode when your client cannot guarantee consistent key ordering.
/v1/sign must be byte-for-byte identical to the body sent to /v1/resolve. Any difference โ including whitespace or key ordering โ will produce a signature mismatch and a 403 response.
/v1/sign must always be called over HTTPS. Never call this endpoint over HTTP โ your api_key is transmitted in the request body. On network failure, retry with the identical payload bytes. Do not reserialize the payload before retrying.
/v1/resolve
Resolve device state
Accepts a signed device state payload. Runs the full multi-signal arbitration algorithm and returns a single authoritative resolved state with confidence scoring, arbitration trace, and conflict analysis. Strictly idempotent for 30 days.
| Header | Required | Value |
|---|---|---|
Content-Type |
Yes | application/json |
X-Signature |
Yes | HMAC-SHA256 hex digest from /v1/sign |
Single device event. Supports the full arbitration algorithm including race condition detection, RF signal analysis, sequence number tracking, and clock drift compensation.
| Field | Type | Required | Description |
|---|---|---|---|
api_key |
string | Yes | Your API key. Use "demo" for testing. |
state.device_id |
string | Yes | Unique device identifier. |
state.status |
string | Yes | Reported device status. Accepted values: online, offline, idle, error, warning, updating, initializing and common aliases. |
state.timestamp |
string | Yes | ISO 8601 event timestamp. e.g. 2026-01-15T14:32:04Z. Used as primary arbitration signal when within drift threshold. |
state.signal_strength |
number | No | RF signal strength in dBm. Enables RF noise analysis. Aliases: rssi, snr. |
state.sequence |
integer | No | Monotonic sequence number. A reset to 0 triggers device restart detection. Aliases: seq, sequence_number. |
state.reconnect_window_seconds |
integer | No | Race condition detection window in seconds. Default: 30. Maximum: 600. Set higher for hardware with slow reconnect cycles (satellite uplinks, deep-sleep devices). The applied value is returned in reconnect_window_seconds. |
state.value |
any | No | Sensor reading or measurement. Passed through to resolved_state.sensor_value. |
state.battery |
number | No | Battery level. Passed through to resolved_state.battery_level. |
state.firmware |
string | No | Firmware version string. Passed through to resolved_state.firmware_version. |
state.location |
string | No | Device location or zone identifier. Passed through to resolved_state.location. |
state.lat / state.lon |
number | No | GPS coordinates. Returned as resolved_state.coordinates: {"lat": ..., "lon": ...} when both are present. |
state.temperature |
number | No | Temperature reading. Passed through. Alias: temp. |
state.humidity |
number | No | Humidity reading. Passed through. |
state.pressure |
number | No | Pressure reading. Passed through. |
Multi-device batch format. Submit multiple events per device for timestamp-based arbitration across event sets. Confidence scoring and drift detection apply per device.
| Field | Type | Required | Description |
|---|---|---|---|
api_key |
string | Yes | Your API key. |
events |
object | Yes | Map of device_id โ array of event objects. Each event object requires timestamp and value. Optional per-event: signal_strength. |
| Field | Type | Always Present | Description |
|---|---|---|---|
status |
string | Yes | success or already_processed |
resolution_id |
string | Yes | Deterministic ID derived from payload hash. Identical payloads always produce the same ID. |
billed |
boolean | Yes | false on trial accounts and duplicate submissions. true on fresh paid resolutions. |
idempotency_expires_at |
string | Yes | ISO 8601 timestamp. After this point the same payload is treated as a new resolution. |
trial_remaining |
integer | Trial only | Free resolutions remaining on trial accounts. |
resolved_state.device_id |
string | Yes | Device identifier echoed from request. |
resolved_state.authoritative_status |
string | Yes | The single resolved truth after full arbitration. One of: online, offline, idle, error, warning, updating, initializing. |
resolved_state.confidence |
float | Yes | Arbitration confidence score 0.20โ1.0. See Confidence Score Interpretation above. |
resolved_state.recommended_action |
string | Yes | Action enum derived from confidence score. ACT (โฅ0.85) โ act autonomously. CONFIRM (0.65โ0.84) โ confirm before acting. LOG_ONLY (0.50โ0.64) โ log and monitor only. LOG_ONLY (0.20โ0.49) โ severe degradation, do not act without manual review. |
resolved_state.arbitration_method |
string | Yes | direct_resolution ยท race_condition_resolution ยท multi_signal_arbitration ยท noise_filtered_resolution ยท drift_compensated_resolution |
resolved_state.arbitration_signals_used |
array | Yes | Ordered list of signals applied during arbitration. e.g. ["event_arrival_time", "device_timestamp", "rf_signal_quality", "reconnect_supersession"] |
resolved_state.deduplication_fingerprint |
string | Yes | 16-char SHA-256 derived payload fingerprint. Use to verify idempotency in your own audit logs. |
resolved_state.race_condition_resolved |
boolean | Yes | true when a late-arriving disconnect event was detected within the reconnect window and overridden by confirmed reconnect. |
resolved_state.clock_drift_compensated |
boolean | Yes | true when device timestamp was outside drift threshold and server-side arrival sequencing was applied instead. |
resolved_state.reconnect_window_seconds |
integer | Yes | The reconnect window applied during this resolution. Confirms what value was used when caller overrides the default. |
resolved_state.resolution_basis |
object | Yes | Contains timestamp_confidence (high/medium/low), signal_quality (strong/moderate/weak/critical), conflicts_resolved (integer count). |
resolved_state.conflicts_detected |
array | When present | Human-readable description of each conflict detected and how it was resolved. Omitted when no conflicts exist. |
resolved_state.event_timestamp |
string | When parseable | Parsed ISO 8601 device timestamp. Omitted when timestamp was unparseable. |
resolved_state.signal_strength_dbm |
number | When submitted | Signal strength echoed from request. |
resolved_state.signal_note |
string | Weak/critical only | Human-readable signal quality note. Only present when signal is weak or critical. |
resolved_state.firmware_version |
string | When submitted | Echoed from request. |
resolved_state.battery_level |
number | When submitted | Echoed from request. |
resolved_state.coordinates |
object | When submitted | {"lat": ..., "lon": ...} โ present only when both lat and lon submitted. |
| Field | Type | Description |
|---|---|---|
resolved_state.[device_id].authoritative_value |
any | Single authoritative value after timestamp arbitration across all submitted events. |
resolved_state.[device_id].confidence |
float | Per-device confidence score 0.20โ1.0. |
resolved_state.[device_id].arbitration_method |
string | timestamp_arbitration or drift_compensated_resolution |
resolved_state.[device_id].deduplication_fingerprint |
string | 16-char idempotency fingerprint for this device's event set. |
resolved_state.[device_id].clock_drift_suspected |
boolean | true when timestamp spread across submitted events exceeded 1 hour. |
resolved_state.[device_id].events_evaluated |
integer | Number of valid events evaluated before reaching the authoritative value. |
| error_code | HTTP | Meaning | Resolution |
|---|---|---|---|
INVALID_SIGNATURE |
403 | X-Signature missing or incorrect | Ensure the body sent to /v1/sign is byte-for-byte identical to the body sent to /v1/resolve. Add header X-Signature-Debug: 1 to receive a signature_debug object in the response with the received body hash, expected signature, and a plain-language hint. |
invalid_api_key |
403 | API key not found | Verify your api_key value. Keys are case-sensitive UUIDs. |
account_inactive |
403 | Account suspended | Check your email for next steps or contact support. |
TRIAL_PAUSED_INACTIVITY |
403 | Trial paused after 7 days without a call | Remaining resolutions preserved. Upgrade to reactivate โ same key, same endpoint, no code changes. |
EMPTY_STATE |
400 | State payload is empty or missing | Provide at minimum: device_id, status, timestamp. |
MISSING_FIELDS |
400 | Required fields missing | Response includes a required_fields array listing exactly what is missing. |
MISSING_EVENTS |
400 | Structured format missing events key | Add an events key containing your device event map. |
INVALID_JSON |
400 | Request body is not valid JSON | Validate JSON before sending. Ensure Content-Type is application/json. |
RATE_LIMITED |
429 | Rate limit exceeded | Back off and retry. Response message specifies which limit was hit. Enterprise plans available for higher limits. |
already_processed |
200 | Duplicate payload within idempotency window | Original resolved_state returned. Not billed. This is expected behavior for retry logic. |
Every resolution includes fields that disclose how events were ordered and whether that ordering is safe to act on in your environment.
| Field | Type | Description |
|---|---|---|
resolution_authority |
string | The signal that determined the final resolution. Values: sequence_number, rf_signal, device_timestamp, clock_drift_compensation, reconnect_window, majority_vote, single_event. |
ordering_mechanism |
string | device_timestamp โ event order determined by device-reported timestamp. server_arrival_sequence โ clock drift detected, arrival order used as fallback. |
ordering_trust |
string | high โ device timestamps used and trustworthy. conditional โ arrival sequencing used due to clock drift; trust depends on broker topology. |
transport_warning |
string | null | Present only when ordering_mechanism is server_arrival_sequence. In environments with asymmetric broker paths or partitioned consumers (MQTT, Kafka), arrival order may not reflect true event order. Gate automated actions on CONFIRM or higher when this field is present. |
/v1/usage
Get account usage
Returns real-time usage data including trial balance, monthly quota, and a 30-day daily breakdown.
| Parameter | Required | Description |
|---|---|---|
api_key |
Yes | Your API key. |
| Field | Type | Description |
|---|---|---|
account_status |
string | active, past_due, or suspended |
trial_remaining |
integer | Free trial resolutions remaining. |
monthly_quota |
integer | Total resolutions allowed per month on your current plan. |
monthly_used |
integer | Resolutions used this billing period. |
daily_usage |
object | {"YYYY-MM-DD": count} โ last 30 days. Only days with at least one resolution are included. |
total_last_30_days |
integer | Sum of all resolutions across the last 30 days. |
| Version | Date | Changes |
|---|---|---|
v1 |
2026-02-26 | Initial versioned release. Per-customer HMAC secrets. Configurable reconnect window. Full arbitration trace in every response. Confidence scoring. Idempotency guarantee with expiry timestamp. Production rate limiting. Activity-based trial expiry. |