Unified API Integration Guide
Overview
The Unified API gives you one endpoint to integrate against, one API key, and one response shape for all of your moderation needs. You moderate by calling a workflow — a named, configured pipeline that we build and run for you.
A workflow can do any of the following, alone or in combination:
- Account-level moderation & fraud detection — deep behavioral and content analysis across everything you know about a user or account (profile, activity, network and device signals, history), returning an automated decision such as
APPROVE/SKIP/BAN. - Content moderation — check one or more pieces of content against your policies.
- Agentic entity review — a deeper review of multi-part entities such as profiles, product listings, or insurance claims, combining their text and media into a single holistic assessment.
- Multi-step pipelines — chain the above with conditional logic. For example, a workflow can first screen a profile photo and only if it looks risky escalate the whole profile to a deep account-level review — all behind one API call.
Content is multimodal: a workflow can take text, images, audio, video, and PDFs, in whatever combination its policies need.
There are two operations:
- Review (
POST /api/v1/review) — submit inputs and get a moderation result back synchronously. - Ingest (
POST /api/v1/ingest) — submit the actions your team took (what was decided) so we can close the loop and improve quality over time. Fire-and-forget.
How workflows work
A workflow encapsulates what gets evaluated and how — which policies and models run, in what order, with what conditional logic, and what the response contains. You don't wire any of that into your code:
- We design, configure, and version your workflow(s) and give each one a name (e.g.
account-review,listing-review,profile-screen). - You call the API with the workflow name and its inputs.
- We confirm the exact inputs and outputs each workflow expects, so you know precisely what to send and what comes back.
Because the workflow holds the configuration, we can tune policies, swap models, or add steps — and publish a new version — without any code change on your side. You keep calling the same workflow name.
Setup
Base URL
The Unified API base URL is:
https://api.musubilabs.ai/api
Interactive API documentation is available at https://api.musubilabs.ai/api/docs.
Authentication
The API is authenticated with an API key passed in the Musubi-Api-Key header:
GET https://api.musubilabs.ai/api/version
accept: application/json
Musubi-Api-Key: [your API key here]
We'll securely provide you with your API key. Your key is tied to your organization, which is how the API knows which workflows and configuration to use — you never pass your organization ID explicitly.
Environments
Environment selection happens per request via the environment field in the request body (env is also accepted). "prod" (case-insensitive) targets production; any other value (e.g. "dev") targets a development backend, for workflows that have one configured. We manage that mapping for you.
The request envelope
Every request — review or ingest — shares the same outer envelope:
| Field | Required | Description |
|---|---|---|
environment |
yes | Target environment. "prod" → production; anything else → dev. (env is accepted as an alias.) |
id |
no | An identifier from your system, echoed back for correlation. |
data |
yes | The workflow payload. data.workflow names the workflow to run; the remaining fields are that workflow's inputs. |
{
"environment": "prod",
"id": "your-correlation-id",
"data": {
"workflow": "your-workflow-name",
"...": "workflow inputs"
}
}
You can name the workflow either in the body (data.workflow, as above) or in the URL: POST /api/v1/review/{workflow_name}. Both run the same workflow.
Calling a workflow — POST /api/v1/review
A review is synchronous: you send the request and the moderation result comes back in the response body.
The inputs inside data are defined by the workflow — we confirm the exact fields for each of yours during onboarding. In practice they're drawn from two kinds of building blocks:
Subject details — a flexible snapshot of the user, account, or entity under review. Provide whatever nested JSON is easiest (account info, profile, recent activity, network/device signals, history). The more relevant signals you include, the better the analysis. Common top-level fields are userId (echoed back as subjectId), eventTime, triggerReason, and comments.
Content — one or more pieces of multimodal content for the workflow's policies to evaluate. A field can hold a single value or a list (e.g. several image URLs evaluated together).
Example: content moderation
A content workflow that checks some text and several images against a policy. Note imageUrls is a list — pass as many as you need and they're assessed together:
curl -X POST 'https://api.musubilabs.ai/api/v1/review' \
-H 'accept: application/json' \
-H 'Musubi-Api-Key: <your-orgs-api-key>' \
-H 'Content-Type: application/json' \
-d '{
"environment": "prod",
"id": "listing-555",
"data": {
"workflow": "image",
"policy": "policy-endpoint",
"text": { "content": "hello world" },
"imageUrls": [
"https://cdn.example.com/img-1.png",
"https://cdn.example.com/img-2.png",
"https://cdn.example.com/img-3.png"
]
}
}'
Example: account-level moderation & fraud detection
A workflow that analyzes everything you know about an account and returns a decision.
{
"environment": "prod",
"id": "report-123",
"data": {
"workflow": "account-review",
"userId": "user-123",
"eventTime": "2024-01-01T00:00:00+00:00",
"triggerReason": "spam",
"comments": "flagged for review",
"account": {"email": "test@example.com", "signupCountry": "US", "accountAgeDays": 3},
"profile": {"name": "Test User", "bio": "..."},
"activity": {"messagesSent": 240, "reportsAgainst": 5}
}
}
Example: a multi-step pipeline
A profile-screen workflow configured to first screen the profile photo, and only escalate to a full account-level review if the photo looks risky. You send one snapshot of the profile; the workflow decides how far to take it and returns a single result.
{
"environment": "prod",
"id": "profile-check-42",
"data": {
"workflow": "profile-screen",
"userId": "user-789",
"eventTime": "2024-01-01T00:00:00+00:00",
"profile": {
"name": "...",
"bio": "...",
"photoUrl": "https://cdn.example.com/u/789/avatar.jpg"
},
"account": {"signupCountry": "US", "accountAgeDays": 1}
}
}
Understanding the response
Every workflow returns the same envelope:
| Field | Description |
|---|---|
id |
The id you supplied, echoed back (empty string if you didn't send one). |
decisionId |
Identifier for the decision, when the workflow produced one; otherwise null. |
status |
The workflow's execution status — success or failed. A failed workflow is returned as HTTP 500 with this same body. |
subjectId |
The subject under review (e.g. the userId you supplied), when applicable. |
eventTime |
When we received the request (UTC, ISO 8601). |
messages |
Informational or warning messages about the response; usually an empty list. |
metadata.result |
The workflow's output. Its shape is defined by the workflow — we confirm it with you. |
metadata.execution |
Engine execution detail (e.g. which steps ran). |
The interesting part is metadata.result — it contains whatever your workflow is configured to produce. A few representative shapes:
A content check surfaces the policy assessment (CLEAR / FLAGGED), the matched label, and confidence as riskScore (0–10):
{
"messages": [],
"id": "listing-555",
"decisionId": null,
"status": "success",
"subjectId": null,
"eventTime": "2024-01-01T00:00:00+00:00",
"metadata": {
"result": {
"data": {
"labels": [
{
"label": "Counterfeit",
"assessment": "FLAGGED",
"labelerOutput": {"label": "Counterfeit", "analysis": "...", "riskScore": 9}
}
]
}
},
"execution": {}
}
}
An account-level decision surfaces a decision type and a model score:
{
"messages": [],
"id": "report-123",
"decisionId": null,
"status": "success",
"subjectId": "user-123",
"eventTime": "2024-01-01T00:00:00+00:00",
"metadata": {
"result": {
"decision": {
"decisionType": {"value": "BAN", "name": "Ban"},
"banReason": {"value": "SPAM", "name": "Spam"},
"extra": {"score": 92}
}
},
"execution": {}
}
}
A multi-step workflow typically returns a compact verdict plus the evidence from each stage — for example:
{
"decision": {"decision": "BAN", "reason": "Counterfeit", "source": "aimod", "confidence": 92},
"evidence": {"aimod": { }, "policyai": { }}
}
We'll give you the exact result shape for each of your workflows so you can parse it directly.
A 200 can still carry a processing error
A 200 with workflow status: "success" means the request was accepted and the workflow ran to completion — it does not guarantee that every step produced a usable decision. A downstream step (PolicyAI or AiMod) can fail in a way the workflow tolerates: the service errored, timed out, or was unavailable, or it responded but reported that it couldn't process the input. In that case the step's result carries a decisionStatus with status: "ERROR" inside metadata.result, even though the HTTP status and the workflow status both still read as success:
{
"messages": [],
"id": "report-123",
"decisionId": null,
"status": "success",
"subjectId": "user-123",
"eventTime": "2024-01-01T00:00:00+00:00",
"metadata": {
"result": {
"decisionStatus": {
"status": "ERROR",
"detail": "The moderation service was unable to process the request."
}
},
"execution": {}
}
}
When the downstream service responds but reports the error itself (rather than the call failing outright), the decisionStatus may also include service-specific identifiers — for example an eventId and a decisionStatus.id from AiMod, alongside a more specific detail.
Either way: when your workflow's result includes a decisionStatus, check decisionStatus.status before treating the result as a decision. Treat ERROR as "no decision" and fall back to human review — the same as a failed status or a timeout. We confirm the exact result shape, including where decisionStatus appears, for each of your workflows.
Submitting feedback — POST /api/v1/ingest
When your moderators take an action, send it to the ingest endpoint. This is a fire-and-forget call that lets us "close the loop" — understanding which decisions were acted on and improving quality over time. It returns a simple acknowledgement.
| Field | Required | Description |
|---|---|---|
workflow |
yes | The workflow this feedback relates to. |
userId |
yes | Unique identifier of the user. |
eventTime |
yes | ISO 8601 timestamp of when the action occurred. |
action |
yes | The action taken, e.g. BAN, APPROVE, UNBAN, WARN. |
moderatorId |
yes | Identifier of the moderator who acted (e.g. email or internal ID). |
moderatorSource |
yes | The group/system the moderator belongs to (e.g. moderation_team). |
comments |
no | Free-text notes about the action. |
decisionId |
no | The decision identifier from a prior review, if this action followed one. |
| (any other field) | no | Captured as subject details. |
{
"environment": "prod",
"id": "action-789",
"data": {
"workflow": "account-review",
"userId": "user-123",
"eventTime": "2024-01-01T00:00:00+00:00",
"action": "BAN",
"moderatorId": "mod-1",
"moderatorSource": "moderation_team",
"comments": "banned for spam"
}
}
{ "id": "evt_01HF...", "status": "ACCEPTED", "messages": [] }
Returning the API's decisions
When you take an action based on a decision the API returned, send it back through ingest with the moderatorId and moderatorSource prefixed with musubi (e.g. musubi_decision). This tells us which recommendations were acted on and feeds back into model training. Include the originating decision identifier when you have it.
Acting on results
A common pattern is to automate the clear-cut cases and route the gray area to humans. Read your workflow's verdict out of metadata.result:
- Classification verdicts: auto-reject when the
assessmentisFLAGGEDandriskScoreis at or above your chosen threshold (e.g. ≥ 8); for lower scores, create a ticket for a human moderator. - Decision verdicts: act on the decision type (
APPROVE/BAN), and passSKIPdecisions to your moderators as usual. - Always fail open to humans. If a request errors, returns
status: "failed", carries an in-banddecisionStatus: "ERROR", or times out, treat it as "no decision" and fall back to your normal moderation flow rather than blocking the user pipeline.
Error handling
The API returns standard HTTP status codes:
| Status | Meaning | Recommended handling |
|---|---|---|
200 |
Success | Process metadata.result. A 200 can still carry an in-band decisionStatus: "ERROR" — see the warning above. |
400 |
Bad request | Fix the request; the response detail explains the problem. |
404 |
Workflow not found | Check the workflow name. |
422 |
Validation error | Fix the request payload; details are forwarded. |
403 |
Forbidden (auth) | Check your Musubi-Api-Key. |
429 |
Too many requests | Back off and retry with exponential backoff. |
500 |
Workflow execution failed | Treat as "no decision"; fall back to human review. The body carries status: "failed". |
502 |
Upstream service error / transient unavailability | Retry briefly; otherwise fall back to human review. |
504 |
Timeout — the request exceeded its time budget | Treat as "no decision"; fall back to human review. |
For review, a downstream model or service that errors, times out, or is unavailable is normally surfaced in-band: an HTTP 200 whose metadata.result carries decisionStatus: "ERROR" (see the warning above). You'll see 502/504 on a review only when the request can't be completed at all or exceeds its overall time budget; transient 5xx/timeout HTTP codes are most common on ingest.
Build your client to fail open — never block content publishing or your moderation queue because a moderation call failed. Retry 429 and transient 502/504 responses with backoff, treat an in-band decisionStatus: "ERROR" the same as a failure, and route anything still unresolved to your normal flow.
Frequently asked questions
Confidence and Risk Score
Classification results always include the assessment (CLEAR / FLAGGED) and the label (the matched category). On top of that, Risk Score is a predefined output ranging from 0 (very certain CLEAR) to 10 (very certain FLAGGED). Use it to automate by threshold: for example, riskScore ≥ 8 → auto-reject, while anything below routes to a human moderator.
Risk Score (and other predefined outputs like analysis, reason, and severityScore, plus any custom outputs) is configured on the policy and surfaced by your workflow in metadata.result. We'll confirm exactly where it appears for your workflow.
Latency
Reviews are synchronous. Latency depends on the model a workflow uses and the modality of the content — text is fastest, and media is slower because it must be fetched and processed. Typical per-check ranges:
| Model | Text | Image | Audio File | Video File |
|---|---|---|---|---|
| Base | 500ms - 1s | 1s - 2s | 3s - 20s | 3s - 20s |
| Core | 900ms - 2s | 2s - 3s | 5s - 20s | 5s - 60s |
| Pro | 1.5s - 15s | 4s - 15s | 5s - 30s | 5s - 120s |
A holistic account review (deep behavioral + content analysis) typically takes around 10s.
Multi-step workflows take longer than single-step ones, since they add up the latency of each stage they run — so the execution budget can reach a few minutes for the most complex pipelines. We recommend a generous client timeout, and designing your pipeline so a slow or failed call falls back to human review rather than blocking. We'll advise on expected latency for your specific workflows.
Rate limits
The Unified API does not add its own rate limit, but under heavy load the backing services can return 429. Treat 429 as a signal to back off and retry with exponential backoff. If you're planning a high-throughput rollout, let us know your expected volumes so we can provision accordingly.
Synchronous vs async / webhooks
POST /api/v1/review is synchronous — the result is in the response. Async execution with webhook callbacks is coming shortly.
Multiple images / list inputs
Content fields can be lists — for example, an image workflow accepts imageUrls as an array, and all of them are evaluated together. See the content moderation example.
Multiple checks per request
A single workflow can run several checks and return them together — that's what workflows are for. We configure your workflow to run the checks you need and shape metadata.result accordingly, whether that's a single overall assessment, several independent labels, or a decision that escalates based on intermediate results.
Country context
If your moderation depends on country — for example, age thresholds that vary by jurisdiction — there's no dedicated country parameter. Include it in your request (in the subject details, or as a content field) and we'll author the workflow's policies to use it. Tell us the specifics of any country-dependent rules so we can build them into your configuration.
Error handling
See the Error handling table. In short: retry 429 (and transient 502/504) with exponential backoff, check auth on 403, fix the request on 400/422/404, treat 500 (status: "failed") — or an in-band decisionStatus: "ERROR" on a 200 — as no decision, and fail open to human review for anything unresolved.
API key management
The Musubi-Api-Key is static — generated once and valid indefinitely, with no automatic expiry or rotation. If you need to rotate a key (e.g. on suspected compromise), contact us and we'll issue a new one.
Tuning in production
Workflows can be tuned after you've shipped your integration, without any code change on your side. They're versioned: we publish a new version with the updated policies, models, or steps, and your calls to the same workflow name pick it up. Earlier versions are retained, so changes are auditable and reversible.
Environments
Separate integration and production environments are supported: select per request with the environment field ("prod" vs anything else), or we set up separate workflows per environment. See Environments.
Workflow setup and ownership
We design, configure, and version your workflows with you — including their policies, steps, inputs, and outputs — and we'll confirm the finalized workflow names and the exact request/response shape for each, so you can plug them straight into your service. Your organization is inferred from your API key, so you never pass an organization ID.
Integration checklist
Auth & setup
- Store your
Musubi-Api-Keysecurely and send it on every request - Confirm your base URL (
https://api.musubilabs.ai/api) and finalized workflow names - Confirm the exact inputs and the
metadata.resultshape for each of your workflows
Review wiring
- Call each workflow by name (
data.workflowor/review/{workflow_name}) with its required inputs - Set
environmentcorrectly per environment - Parse your verdict out of
metadata.result
Feedback wiring
- Send an ingest event for every moderator action
- Prefix
moderatorId/moderatorSourcewithmusubifor actions taken on the API's decisions
Acting & automation
- Define your auto-reject vs human-review thresholds (e.g.
riskScore≥ 8) - Pass low-confidence /
SKIPresults to moderators - Build in safety checks before automated actioning
Error handling
- Use a generous client timeout
- Retry
429/transient5xxwith exponential backoff - Fail open to human review on
failed, errors, or timeouts