BetaThe V4 API is in beta — endpoints and functionality may change.
Analyticsv2

Get risk-tolerance report

GET/analytics/risk-tolerance

Returns 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

AuthorizationRequiredstring

Your API key, sent as a bearer token: Authorization: Bearer <api_key>. Editing this stays in sync with the API key box on the right.

Bearer

Query parameters

daysoptionalinteger

Length of the reporting window in days, ending now. Between 1 and 365; defaults to 30. Ignored when from/to are supplied.

fromoptionalinteger

Window start as a Unix epoch timestamp in milliseconds. When supplied, overrides days.

tooptionalinteger

Window end as a Unix epoch timestamp in milliseconds. Defaults to now.

computeoptionalboolean

Set 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.

Array of RiskPhoneReport

phone_numberoptionalstring
allocation_idoptionalstring
risk_scoreoptionalnumber

Overall risk score, 0 (lowest) to 100 (highest). Clamped sum of the ten risk_factors.

risk_leveloptionalstring

Banding of risk_score: low = 0-29 (healthy), medium = 30-59 (monitor), high = 60-100 (likely to be filtered/suspended by carriers).

"low""medium""high"
metricsoptionalRiskMetrics

Raw 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_dayoptionalnumber

New outbound-initiated conversations started in the window / period_days. High values indicate cold-outreach volume (risk-raising).

avg_inbound_conversations_per_dayoptionalnumber

New inbound-initiated conversations (customer messaged first) in the window / period_days. High values indicate organic demand (risk-lowering).

inbound_initiated_percentageoptionalnumber

Share 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_contactsoptionalinteger

Distinct contacts (conversation keys) messaged in the window. Large lists suggest broadcast/spam usage.

reply_rateoptionalnumber

Percentage 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_conversationoptionalnumber

Mean inbound messages per conversation in the window. Below 3 (with >= 3 conversations) indicates shallow, low-engagement outreach.

inbound_outbound_message_ratiooptionalnumber

Inbound 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_houroptionalinteger

Peak 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_houroptionalinteger

Peak outbound messages sent in any single UTC hour of the window (burst detection). Sustained rates above ~100/hour signal automation.

message_similarity_percentageoptionalnumber | null

Templated-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_initiatedoptionalinteger

Count of new conversations the customer started in the window.

outbound_initiatedoptionalinteger

Count of new conversations this number started in the window.

total_conversationsoptionalinteger

Conversations with any message activity in the window.

total_messagesoptionalinteger

Inbound + outbound messages in the window (excludes deleted).

total_inbound_messagesoptionalinteger

Inbound message count in the window.

total_outbound_messagesoptionalinteger

Outbound message count in the window.

avg_messages_per_dayoptionalnumber

total_messages / period_days.

avg_conversation_depthoptionalnumber

total_messages / total_conversations (0 when there are no conversations).

risk_factorsoptionalRiskFactors

The 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).

outboundConversationsoptionalRiskFactor

Avg outbound-initiated conversations/day. Scoring: >50 = +30, 40-50 = +20, 30-40 = +8, else 0. Cold-outreach volume.

valueoptionalnumber | null

The measured value for this factor (units depend on the factor).

scoreoptionalnumber

Points this factor contributed to the total risk score.

statusoptionalstring

Qualitative status for display.

"ok""bonus""warning""high""insufficient_data"
impactoptionalstring

Relative weight of this factor in the overall score.

"low""medium""high""very_high"
sampleSizeoptionalinteger

Sample size backing the value, when the factor guards on a minimum sample.

inboundConversationsoptionalRiskFactor

Avg inbound-initiated conversations/day (bonus). Scoring: >=100 = -20, 50-100 = -15, 30-50 = -10, 15-30 = -5, else 0. Organic demand lowers risk.

valueoptionalnumber | null

The measured value for this factor (units depend on the factor).

scoreoptionalnumber

Points this factor contributed to the total risk score.

statusoptionalstring

Qualitative status for display.

"ok""bonus""warning""high""insufficient_data"
impactoptionalstring

Relative weight of this factor in the overall score.

"low""medium""high""very_high"
sampleSizeoptionalinteger

Sample size backing the value, when the factor guards on a minimum sample.

inboundPercentageoptionalRiskFactor

Percentage 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 | null

The measured value for this factor (units depend on the factor).

scoreoptionalnumber

Points this factor contributed to the total risk score.

statusoptionalstring

Qualitative status for display.

"ok""bonus""warning""high""insufficient_data"
impactoptionalstring

Relative weight of this factor in the overall score.

"low""medium""high""very_high"
sampleSizeoptionalinteger

Sample size backing the value, when the factor guards on a minimum sample.

totalContactsoptionalRiskFactor

Distinct contacts messaged in the window. Scoring: >2000 = +15, 1500-2000 = +8, else 0.

valueoptionalnumber | null

The measured value for this factor (units depend on the factor).

scoreoptionalnumber

Points this factor contributed to the total risk score.

statusoptionalstring

Qualitative status for display.

"ok""bonus""warning""high""insufficient_data"
impactoptionalstring

Relative weight of this factor in the overall score.

"low""medium""high""very_high"
sampleSizeoptionalinteger

Sample size backing the value, when the factor guards on a minimum sample.

replyRateoptionalRiskFactor

Percentage 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 | null

The measured value for this factor (units depend on the factor).

scoreoptionalnumber

Points this factor contributed to the total risk score.

statusoptionalstring

Qualitative status for display.

"ok""bonus""warning""high""insufficient_data"
impactoptionalstring

Relative weight of this factor in the overall score.

"low""medium""high""very_high"
sampleSizeoptionalinteger

Sample size backing the value, when the factor guards on a minimum sample.

inboundMsgCountoptionalRiskFactor

Avg inbound messages per conversation. Scoring: <3 = +25 (with >= 3 conversations), else 0.

valueoptionalnumber | null

The measured value for this factor (units depend on the factor).

scoreoptionalnumber

Points this factor contributed to the total risk score.

statusoptionalstring

Qualitative status for display.

"ok""bonus""warning""high""insufficient_data"
impactoptionalstring

Relative weight of this factor in the overall score.

"low""medium""high""very_high"
sampleSizeoptionalinteger

Sample size backing the value, when the factor guards on a minimum sample.

inboundOutboundRatiooptionalRiskFactor

Inbound share of all messages. Scoring: <=10% = +20, 11-29% = +10, >=30% = 0. Requires >= 10 messages to score. Detects one-way traffic.

valueoptionalnumber | null

The measured value for this factor (units depend on the factor).

scoreoptionalnumber

Points this factor contributed to the total risk score.

statusoptionalstring

Qualitative status for display.

"ok""bonus""warning""high""insufficient_data"
impactoptionalstring

Relative weight of this factor in the overall score.

"low""medium""high""very_high"
sampleSizeoptionalinteger

Sample size backing the value, when the factor guards on a minimum sample.

newConversationBurstsoptionalRiskFactor

Peak 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 | null

The measured value for this factor (units depend on the factor).

scoreoptionalnumber

Points this factor contributed to the total risk score.

statusoptionalstring

Qualitative status for display.

"ok""bonus""warning""high""insufficient_data"
impactoptionalstring

Relative weight of this factor in the overall score.

"low""medium""high""very_high"
sampleSizeoptionalinteger

Sample size backing the value, when the factor guards on a minimum sample.

messageBurstsoptionalRiskFactor

Peak outbound messages in any single UTC hour. Scoring: >200 = +30, 150-200 = +20, 100-150 = +10, else 0.

valueoptionalnumber | null

The measured value for this factor (units depend on the factor).

scoreoptionalnumber

Points this factor contributed to the total risk score.

statusoptionalstring

Qualitative status for display.

"ok""bonus""warning""high""insufficient_data"
impactoptionalstring

Relative weight of this factor in the overall score.

"low""medium""high""very_high"
sampleSizeoptionalinteger

Sample size backing the value, when the factor guards on a minimum sample.

messageSimilarityoptionalRiskFactor

Largest 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 | null

The measured value for this factor (units depend on the factor).

scoreoptionalnumber

Points this factor contributed to the total risk score.

statusoptionalstring

Qualitative status for display.

"ok""bonus""warning""high""insufficient_data"
impactoptionalstring

Relative weight of this factor in the overall score.

"low""medium""high""very_high"
sampleSizeoptionalinteger

Sample size backing the value, when the factor guards on a minimum sample.

daily_dataoptionalRiskDailyData
outbound_conversationsoptionalRiskDailySeriesPoint[]

Array of RiskDailySeriesPoint

dateoptionalstring

UTC calendar day (YYYY-MM-DD).

countoptionalinteger
inbound_conversationsoptionalRiskDailySeriesPoint[]

Array of RiskDailySeriesPoint

dateoptionalstring

UTC calendar day (YYYY-MM-DD).

countoptionalinteger
period_daysoptionalinteger
windowoptionalRiskWindow

The concrete time window the report was computed over.

fromoptionalinteger

Window start (Unix epoch ms).

tooptionalinteger

Window end (Unix epoch ms).

is_defaultoptionalboolean

True when the caller supplied no window params (default 30 days).

calculated_atoptionalinteger

When the report was computed (Unix epoch ms).

computed_with_expensive_metricsoptionalboolean

Whether the message-similarity metric was included.

Response codes

200Risk report for the organization's dedicated numbers
401Authentication required or invalid
403Organization has no dedicated numbers (`dedicated_plan_required`)

Sends a live request with your values and shows the real response below. Your key is stored only in this browser.

Request
curl -X GET https://api.blooio.com/v2/api/analytics/risk-tolerance?days=30&from=0&to=0&compute=false \
Response objectexample
{  "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}