Get risk-tolerance report
/analytics/risk-toleranceReturns the per-number risk-tolerance report for every dedicated number in the organization over a bounded time window. Numbers are returned sorted by descending risk score (riskiest first). Each entry carries a risk_score (0-100), a risk_level (low/medium/high), the raw metrics used to derive it, the ten scored risk_factors (value + points + status per factor), and a daily_data time series of new inbound/outbound conversations for charting. Scope — Org-scoped: any API key in the organization returns the full dedicated fleet, regardless of which numbers the key is attached to. Eligibility — Requires at least one dedicated number (Dedicated Commercial or Dedicated Enterprise). Organizations with only shared, trial, or inbound numbers receive 403 dedicated_plan_required. Computation — Real-time; recomputed from Postgres on every request (no cache). Definitions used across the metrics: a conversation is keyed by contact (or group); a new conversation is one whose all-time first message falls inside the window; a reply is a conversation that received 3 or more inbound messages; bursts are the peak count in any single UTC hour of the window. The expensive message-similarity (SimHash) metric is included automatically for fleets of 5 numbers or fewer, and otherwise only when compute=true (larger fleets return message_similarity_percentage: null unless requested).
Headers
AuthorizationRequiredstringYour API key, sent as a bearer token: Authorization: Bearer <api_key>. Editing this stays in sync with the API key box on the right.
Query parameters
daysoptionalintegerLength of the reporting window in days, ending now. Between 1 and 365; defaults to 30. Ignored when from/to are supplied.
fromoptionalintegerWindow start as a Unix epoch timestamp in milliseconds. When supplied, overrides days.
tooptionalintegerWindow end as a Unix epoch timestamp in milliseconds. Defaults to now.
computeoptionalbooleanSet to true to force the expensive message-similarity metric. Small fleets (<= 5 numbers) always compute it; larger fleets omit it unless this flag is set, so message_similarity_percentage may be null otherwise.
Returns
phone_numbersoptionalRiskPhoneReport[]Per-number reports, sorted by descending risk score.
phone_numbersoptionalRiskPhoneReport[]Per-number reports, sorted by descending risk score.
Array of RiskPhoneReport
phone_numberoptionalstringallocation_idoptionalstringrisk_scoreoptionalnumberOverall risk score, 0 (lowest) to 100 (highest). Clamped sum of the ten risk_factors.
risk_leveloptionalstringBanding of risk_score: low = 0-29 (healthy), medium = 30-59 (monitor), high = 60-100 (likely to be filtered/suspended by carriers).
"low""medium""high"metricsoptionalRiskMetricsRaw and derived usage metrics for a number over the window. A *conversation* is keyed by contact (1:1) or group; a *new conversation* is one whose all-time first message falls inside the window; a *reply* is a conversation that received 3 or more inbound messages. Averages use the configured window length (period_days), not the number of days that actually had activity, so a number that was quiet for half the period isn't inflated.
metricsoptionalRiskMetricsRaw and derived usage metrics for a number over the window. A *conversation* is keyed by contact (1:1) or group; a *new conversation* is one whose all-time first message falls inside the window; a *reply* is a conversation that received 3 or more inbound messages. Averages use the configured window length (period_days), not the number of days that actually had activity, so a number that was quiet for half the period isn't inflated.
avg_outbound_conversations_per_dayoptionalnumberNew outbound-initiated conversations started in the window / period_days. High values indicate cold-outreach volume (risk-raising).
avg_inbound_conversations_per_dayoptionalnumberNew inbound-initiated conversations (customer messaged first) in the window / period_days. High values indicate organic demand (risk-lowering).
inbound_initiated_percentageoptionalnumberShare of new conversations that the customer started, as a percentage: inbound_initiated / (inbound_initiated + outbound_initiated) * 100. Higher is healthier. Only scored once there are >= 5 new conversations.
total_contactsoptionalintegerDistinct contacts (conversation keys) messaged in the window. Large lists suggest broadcast/spam usage.
reply_rateoptionalnumberPercentage of conversations that received a meaningful reply, defined as 3 or more inbound messages: conversations_with_3plus_inbound / total_conversations * 100. One of the strongest spam signals; only scored once there are >= 10 conversations.
avg_inbound_messages_per_conversationoptionalnumberMean inbound messages per conversation in the window. Below 3 (with >= 3 conversations) indicates shallow, low-engagement outreach.
inbound_outbound_message_ratiooptionalnumberInbound messages as a percentage of all messages: total_inbound_messages / total_messages * 100. Low values mean one-way (monologuing) traffic. Only scored once there are >= 10 messages.
max_new_conversations_per_houroptionalintegerPeak number of NEW conversations started in any single UTC hour of the window (burst detection). A human pace is roughly <= 15/hour; sustained higher rates signal automation.
max_messages_per_houroptionalintegerPeak outbound messages sent in any single UTC hour of the window (burst detection). Sustained rates above ~100/hour signal automation.
message_similarity_percentageoptionalnumber | nullTemplated-content signal: the largest cluster of near-identical outbound messages (by SimHash) as a percentage of qualifying messages. Only outbound messages with >= 50 characters count, and a valid ratio needs >= 20 qualifying messages. null when not computed (large fleet without compute=true) or below the minimum sample.
inbound_initiatedoptionalintegerCount of new conversations the customer started in the window.
outbound_initiatedoptionalintegerCount of new conversations this number started in the window.
total_conversationsoptionalintegerConversations with any message activity in the window.
total_messagesoptionalintegerInbound + outbound messages in the window (excludes deleted).
total_inbound_messagesoptionalintegerInbound message count in the window.
total_outbound_messagesoptionalintegerOutbound message count in the window.
avg_messages_per_dayoptionalnumbertotal_messages / period_days.
avg_conversation_depthoptionalnumbertotal_messages / total_conversations (0 when there are no conversations).
risk_factorsoptionalRiskFactorsThe ten additive risk factors that sum (clamped to 0-100) into risk_score. Each factor reports its measured value, the score (points) it contributed, a status, and its impact weight. Positive scores raise risk; negative scores are bonuses. Some factors only score once a minimum sample is reached (otherwise status: insufficient_data, score: 0).
risk_factorsoptionalRiskFactorsThe ten additive risk factors that sum (clamped to 0-100) into risk_score. Each factor reports its measured value, the score (points) it contributed, a status, and its impact weight. Positive scores raise risk; negative scores are bonuses. Some factors only score once a minimum sample is reached (otherwise status: insufficient_data, score: 0).
outboundConversationsoptionalRiskFactorAvg outbound-initiated conversations/day. Scoring: >50 = +30, 40-50 = +20, 30-40 = +8, else 0. Cold-outreach volume.
outboundConversationsoptionalRiskFactorAvg outbound-initiated conversations/day. Scoring: >50 = +30, 40-50 = +20, 30-40 = +8, else 0. Cold-outreach volume.
valueoptionalnumber | nullThe measured value for this factor (units depend on the factor).
scoreoptionalnumberPoints this factor contributed to the total risk score.
statusoptionalstringQualitative status for display.
"ok""bonus""warning""high""insufficient_data"impactoptionalstringRelative weight of this factor in the overall score.
"low""medium""high""very_high"sampleSizeoptionalintegerSample size backing the value, when the factor guards on a minimum sample.
inboundConversationsoptionalRiskFactorAvg inbound-initiated conversations/day (bonus). Scoring: >=100 = -20, 50-100 = -15, 30-50 = -10, 15-30 = -5, else 0. Organic demand lowers risk.
inboundConversationsoptionalRiskFactorAvg inbound-initiated conversations/day (bonus). Scoring: >=100 = -20, 50-100 = -15, 30-50 = -10, 15-30 = -5, else 0. Organic demand lowers risk.
valueoptionalnumber | nullThe measured value for this factor (units depend on the factor).
scoreoptionalnumberPoints this factor contributed to the total risk score.
statusoptionalstringQualitative status for display.
"ok""bonus""warning""high""insufficient_data"impactoptionalstringRelative weight of this factor in the overall score.
"low""medium""high""very_high"sampleSizeoptionalintegerSample size backing the value, when the factor guards on a minimum sample.
inboundPercentageoptionalRiskFactorPercentage of new conversations the customer started (gradient). Scoring ranges from -15 (>=90% inbound) up to +20 (<20% inbound). Requires >= 5 new conversations to score.
inboundPercentageoptionalRiskFactorPercentage of new conversations the customer started (gradient). Scoring ranges from -15 (>=90% inbound) up to +20 (<20% inbound). Requires >= 5 new conversations to score.
valueoptionalnumber | nullThe measured value for this factor (units depend on the factor).
scoreoptionalnumberPoints this factor contributed to the total risk score.
statusoptionalstringQualitative status for display.
"ok""bonus""warning""high""insufficient_data"impactoptionalstringRelative weight of this factor in the overall score.
"low""medium""high""very_high"sampleSizeoptionalintegerSample size backing the value, when the factor guards on a minimum sample.
totalContactsoptionalRiskFactorDistinct contacts messaged in the window. Scoring: >2000 = +15, 1500-2000 = +8, else 0.
totalContactsoptionalRiskFactorDistinct contacts messaged in the window. Scoring: >2000 = +15, 1500-2000 = +8, else 0.
valueoptionalnumber | nullThe measured value for this factor (units depend on the factor).
scoreoptionalnumberPoints this factor contributed to the total risk score.
statusoptionalstringQualitative status for display.
"ok""bonus""warning""high""insufficient_data"impactoptionalstringRelative weight of this factor in the overall score.
"low""medium""high""very_high"sampleSizeoptionalintegerSample size backing the value, when the factor guards on a minimum sample.
replyRateoptionalRiskFactorPercentage of conversations with 3+ inbound messages. Scoring: <20% = +35, 20-30% = +20, 30-40% = +8, >=50% = -5. Requires >= 10 conversations to score. Highest-weight (very_high) factor.
replyRateoptionalRiskFactorPercentage of conversations with 3+ inbound messages. Scoring: <20% = +35, 20-30% = +20, 30-40% = +8, >=50% = -5. Requires >= 10 conversations to score. Highest-weight (very_high) factor.
valueoptionalnumber | nullThe measured value for this factor (units depend on the factor).
scoreoptionalnumberPoints this factor contributed to the total risk score.
statusoptionalstringQualitative status for display.
"ok""bonus""warning""high""insufficient_data"impactoptionalstringRelative weight of this factor in the overall score.
"low""medium""high""very_high"sampleSizeoptionalintegerSample size backing the value, when the factor guards on a minimum sample.
inboundMsgCountoptionalRiskFactorAvg inbound messages per conversation. Scoring: <3 = +25 (with >= 3 conversations), else 0.
inboundMsgCountoptionalRiskFactorAvg inbound messages per conversation. Scoring: <3 = +25 (with >= 3 conversations), else 0.
valueoptionalnumber | nullThe measured value for this factor (units depend on the factor).
scoreoptionalnumberPoints this factor contributed to the total risk score.
statusoptionalstringQualitative status for display.
"ok""bonus""warning""high""insufficient_data"impactoptionalstringRelative weight of this factor in the overall score.
"low""medium""high""very_high"sampleSizeoptionalintegerSample size backing the value, when the factor guards on a minimum sample.
inboundOutboundRatiooptionalRiskFactorInbound share of all messages. Scoring: <=10% = +20, 11-29% = +10, >=30% = 0. Requires >= 10 messages to score. Detects one-way traffic.
inboundOutboundRatiooptionalRiskFactorInbound share of all messages. Scoring: <=10% = +20, 11-29% = +10, >=30% = 0. Requires >= 10 messages to score. Detects one-way traffic.
valueoptionalnumber | nullThe measured value for this factor (units depend on the factor).
scoreoptionalnumberPoints this factor contributed to the total risk score.
statusoptionalstringQualitative status for display.
"ok""bonus""warning""high""insufficient_data"impactoptionalstringRelative weight of this factor in the overall score.
"low""medium""high""very_high"sampleSizeoptionalintegerSample size backing the value, when the factor guards on a minimum sample.
newConversationBurstsoptionalRiskFactorPeak new conversations started in any single UTC hour. Scoring: >60 = +40, 40-60 = +30, 25-40 = +20, 15-25 = +10, else 0. Very-high weight; hallmark of mass-outreach automation.
newConversationBurstsoptionalRiskFactorPeak new conversations started in any single UTC hour. Scoring: >60 = +40, 40-60 = +30, 25-40 = +20, 15-25 = +10, else 0. Very-high weight; hallmark of mass-outreach automation.
valueoptionalnumber | nullThe measured value for this factor (units depend on the factor).
scoreoptionalnumberPoints this factor contributed to the total risk score.
statusoptionalstringQualitative status for display.
"ok""bonus""warning""high""insufficient_data"impactoptionalstringRelative weight of this factor in the overall score.
"low""medium""high""very_high"sampleSizeoptionalintegerSample size backing the value, when the factor guards on a minimum sample.
messageBurstsoptionalRiskFactorPeak outbound messages in any single UTC hour. Scoring: >200 = +30, 150-200 = +20, 100-150 = +10, else 0.
messageBurstsoptionalRiskFactorPeak outbound messages in any single UTC hour. Scoring: >200 = +30, 150-200 = +20, 100-150 = +10, else 0.
valueoptionalnumber | nullThe measured value for this factor (units depend on the factor).
scoreoptionalnumberPoints this factor contributed to the total risk score.
statusoptionalstringQualitative status for display.
"ok""bonus""warning""high""insufficient_data"impactoptionalstringRelative weight of this factor in the overall score.
"low""medium""high""very_high"sampleSizeoptionalintegerSample size backing the value, when the factor guards on a minimum sample.
messageSimilarityoptionalRiskFactorLargest near-identical outbound cluster (SimHash) as a percentage of qualifying messages (>= 50 chars). Scoring: >70% = +15, 50-70% = +8, <30% = -2. Only evaluated with >= 20 qualifying messages and when the expensive metric was computed; otherwise value: null, score: 0.
messageSimilarityoptionalRiskFactorLargest near-identical outbound cluster (SimHash) as a percentage of qualifying messages (>= 50 chars). Scoring: >70% = +15, 50-70% = +8, <30% = -2. Only evaluated with >= 20 qualifying messages and when the expensive metric was computed; otherwise value: null, score: 0.
valueoptionalnumber | nullThe measured value for this factor (units depend on the factor).
scoreoptionalnumberPoints this factor contributed to the total risk score.
statusoptionalstringQualitative status for display.
"ok""bonus""warning""high""insufficient_data"impactoptionalstringRelative weight of this factor in the overall score.
"low""medium""high""very_high"sampleSizeoptionalintegerSample size backing the value, when the factor guards on a minimum sample.
daily_dataoptionalRiskDailyData
daily_dataoptionalRiskDailyDataoutbound_conversationsoptionalRiskDailySeriesPoint[]
outbound_conversationsoptionalRiskDailySeriesPoint[]Array of RiskDailySeriesPoint
dateoptionalstringUTC calendar day (YYYY-MM-DD).
countoptionalintegerinbound_conversationsoptionalRiskDailySeriesPoint[]
inbound_conversationsoptionalRiskDailySeriesPoint[]Array of RiskDailySeriesPoint
dateoptionalstringUTC calendar day (YYYY-MM-DD).
countoptionalintegerperiod_daysoptionalintegerwindowoptionalRiskWindowThe concrete time window the report was computed over.
windowoptionalRiskWindowThe concrete time window the report was computed over.
fromoptionalintegerWindow start (Unix epoch ms).
tooptionalintegerWindow end (Unix epoch ms).
is_defaultoptionalbooleanTrue when the caller supplied no window params (default 30 days).
calculated_atoptionalintegerWhen the report was computed (Unix epoch ms).
computed_with_expensive_metricsoptionalbooleanWhether the message-similarity metric was included.
Response codes
Sends a live request with your values and shows the real response below. Your key is stored only in this browser.
curl -X GET https://api.blooio.com/v2/api/analytics/risk-tolerance?days=30&from=0&to=0&compute=false \{ "phone_numbers": [ { "phone_number": "+15551234567", "allocation_id": "obj_a1b2c3d4", "risk_score": 0, "risk_level": "low", "metrics": { "avg_outbound_conversations_per_day": 0, "avg_inbound_conversations_per_day": 0, "inbound_initiated_percentage": 0, "total_contacts": 3, "reply_rate": 0, "avg_inbound_messages_per_conversation": 0, "inbound_outbound_message_ratio": 0, "max_new_conversations_per_hour": 0, "max_messages_per_hour": 0, "message_similarity_percentage": 0, "inbound_initiated": 0, "outbound_initiated": 0, "total_conversations": 3, "total_messages": 3, "total_inbound_messages": 3, "total_outbound_messages": 3, "avg_messages_per_day": 0, "avg_conversation_depth": 0 }, "risk_factors": { "outboundConversations": { "value": 0, "score": 0, "status": "ok", "impact": "low", "sampleSize": 50 }, "inboundConversations": { "value": 0, "score": 0, "status": "ok", "impact": "low", "sampleSize": 50 }, "inboundPercentage": { "value": 0, "score": 0, "status": "ok", "impact": "low", "sampleSize": 50 }, "totalContacts": { "value": 0, "score": 0, "status": "ok", "impact": "low", "sampleSize": 50 }, "replyRate": { "value": 0, "score": 0, "status": "ok", "impact": "low", "sampleSize": 50 }, "inboundMsgCount": { "value": 0, "score": 0, "status": "ok", "impact": "low", "sampleSize": 50 }, "inboundOutboundRatio": { "value": 0, "score": 0, "status": "ok", "impact": "low", "sampleSize": 50 }, "newConversationBursts": { "value": 0, "score": 0, "status": "ok", "impact": "low", "sampleSize": 50 }, "messageBursts": { "value": 0, "score": 0, "status": "ok", "impact": "low", "sampleSize": 50 }, "messageSimilarity": { "value": 0, "score": 0, "status": "ok", "impact": "low", "sampleSize": 50 } }, "daily_data": { "outbound_conversations": [ { "date": "2026-07-01", "count": 3 } ], "inbound_conversations": [ { "date": "2026-07-01", "count": 3 } ] } } ], "period_days": 0, "window": { "from": 0, "to": 0, "is_default": true }, "calculated_at": 37.7749, "computed_with_expensive_metrics": false}