Operator-reviewed integration preview
Integrate Solana risk evidence without hiding uncertainty
Use your backend to read existing assessments, display their time and limitations, and receive authenticated risk-change events. GET requests never collect fresh chain data.
RugSignal developer notes · Published · Source-based integration documentation.
Live commercial API access and redistribution require operator approval and a provider-rights review. Consumer Pro access does not issue API credentials. No public commercial API pricing, automatic key issuance or unrestricted SDK licence is offered.
1. Keep the credential on your server
Request an integration review for one concrete read-only use case. Approved clients receive a privately delivered, expiring bearer key, explicit scopes, client quotas and an operator-provided integration kit. Keep the key out of browser bundles, URLs and logs. Use HTTPS; sandbox credentials cannot authenticate production.
For an approved HTTP client, send Authorization: Bearer $RUGSIGNAL_API_KEY to GET /api/v1/risk/{mint}. The public OpenAPI 3.1 specification describes the versioned routes and response schemas. Wallet signatures and API keys grant different access.
Run the supplied TypeScript SDK from Node 22
The provided kit already contains compiled JavaScript and type declarations. Save this JavaScript example as quickstart.mjs beside the extracted sdk directory and run node quickstart.mjs after setting your privately issued key in the process environment. No npm registry package or source repository is required. The example uses canonical Wrapped SOL to demonstrate reference-asset handling; it is not an ordinary-token safety example.
import { RugSignalClient, RiskAPIError } from './sdk/dist/index.js';
const apiKey = process.env.RUGSIGNAL_API_KEY;
if (!apiKey) throw new Error('Set a privately issued server credential.');
const client = new RugSignalClient({
baseUrl: process.env.RUGSIGNAL_ORIGIN ?? 'https://rugsignal.com',
apiKey,
timeoutMs: 8000,
retries: 1,
});
try {
const assessment = await client.risk('So11111111111111111111111111111111111111112');
const interpretation = assessment.assessmentScope === 'reference-context'
? 'Reference asset context; ordinary token rating is not applicable'
: assessment.level + ' deterministic risk category';
console.log({
interpretation,
synthetic: assessment.synthetic,
observedAt: assessment.observedAt,
stale: assessment.freshness.stale,
evidenceCoverage: assessment.coverage,
unknownChecks: assessment.unknownChecks,
limitations: assessment.limitations,
reportUrl: assessment.reportUrl,
});
} catch (error) {
if (!(error instanceof RiskAPIError)) throw error;
console.error({ status: error.status, code: error.code, requestId: error.requestId });
process.exitCode = 1;
}2. Display evidence, unknowns and applicability together
observedAt and scoringVersion identify the retained observation and its policy. coverage measures evidence coverage, never detection accuracy. unknownChecks are not passes. Preserve findings, their references and limitations; do not turn a missing response into Low. Independent predictive accuracy remains unverified.
assessmentScope: reference-context means the canonical native or issuer reference uses separate applicability rules; the retained historical level is not an ordinary-token prediction. A name or ticker does not establish this identity. Display synthetic visibly in any sandbox example or embed.
A response becomes marked stale after 300 seconds, even though public token-page indexing uses a separate quality policy. Reading again does not refresh the underlying observation. Optional ?observation=UUID selects a retained observation. The evidence route returns derived references and metadata, not unrestricted upstream payloads.
Read /history and /changes under the same mint with limit from 1 to 50. Pass the returned nextCursor unchanged; stop when it is null. Never lowercase a case-sensitive Solana mint. Send your server-fetched assessment to the supplied renderRiskCard component; keep live credentials on your backend.
3. Handle limits and failures explicitly
| Status | Meaning and action |
|---|---|
| 400 / 413 / 415 | Invalid input, oversized body or wrong content type. Fix the request. |
| 401 / 403 | Invalid or expired key, missing scope, rights or environment permission. Do not retry unchanged. |
| 404 | No stored or accessible resource. Show unavailable, never low risk. |
| 409 | Idempotency conflict. Review the original request; do not blindly change its key. |
| 429 | Allowance reached. Respect Retry-After; avoid a retry loop. |
| 503 | Integration inactive or unavailable. Preserve an honest unavailable state. |
Errors include error.code, error.message and error.requestId; responses carry a request ID and schema version. Log request IDs and status, never credentials or private payloads. Responses are private/no-store; avoid shared authenticated caches. Default client limits are 30 requests/minute and 1,000/day, subject to configured client and shared admission. Do not assume a quota entitlement. The SDK uses an eight-second default timeout and at most two configured transient retries; long Retry-After returns an error for your application to schedule safely.
4. Verify signed risk-change events
An approved client with subscriptions:write can POST a mint and operator-approved HTTPS endpoint to /api/v1/subscriptions with an Idempotency-Key. Store the first response’s signingSecret privately; an idempotent replay does not return it again. If that response was lost, list and disable the subscription, then recreate deliberately with a new key. Subscribing does not start monitoring or guarantee event coverage.
Read X-RugSignal-Signature and verify its HMAC over the exact raw body before parsing JSON. The following helper matches the sender contract; your receiver must also validate the event schema, expected mint and environment, and durably deduplicate stable event IDs.
import { createHmac, timingSafeEqual } from 'node:crypto';
export function verifyRiskEvent(rawBody, signature, secret, now = Date.now()) {
if (typeof rawBody !== 'string' || typeof signature !== 'string' ||
typeof secret !== 'string' || !secret) return false;
const match = /^t=(\d{10}),v1=([a-f0-9]{64})$/.exec(signature);
if (!match || Math.abs(now / 1000 - Number(match[1])) > 300) return false;
const expected = createHmac('sha256', secret)
.update(match[1] + '.' + rawBody).digest();
return timingSafeEqual(expected, Buffer.from(match[2], 'hex'));
}
// Read at most 8 KiB of the EXACT raw body before JSON parsing.
// verifyRiskEvent(rawBody, request.headers['x-rugsignal-signature'], secret)
// If invalid: reject. If valid: parse/validate the event's schema and environment.
// In one database transaction: insert UNIQUE(event.id) and durably accept work.
// Duplicate accepted event => 2xx. Acknowledge only after durable acceptance.
// A signature alone does not provide replay protection or exactly-once delivery.Delivery is at least once with six bounded attempts and exponential backoff; delayed, duplicate or unacknowledged delivery is possible. Accept duplicate already-committed event IDs with 2xx. Source/parser updates are not represented as chain-risk events. Use owned delivery history to investigate failures; a signature is not evidence that every chain change was observed.
5. Test before requesting live access
The operator-provided private sandbox exercises the actual SDK, API authorization, persistence and local signed-event capture with visibly synthetic examples. Its database, queues and credentials are isolated; external callbacks and transactions are blocked. Request access through the integration review rather than guessing a production key. Live compatibility, redistribution rights and your receiver’s behavior require a separate approved acceptance test.
Request an operator-reviewed integration · Integration overview · Evidence methodology · Accuracy and limitations