Skip to content

PolicyAI v1 to v2 API Migration Guide

This guide helps you migrate from PolicyAI v1 API endpoints to the newer v2 API.

Why the change? v2 offers more flexibility:

  • Simpler policy references: Use human-readable policy keys instead of UUIDs
  • Richer output: Labels include more detailed output fields from the model
  • Labeling Endpoints: Bundle one or more policies into a named, reusable endpoint — evaluate content against multiple policies in a single API call
  • Multimodal Support: Moderate text and images together against your policies, with video and audio support coming soon!
  • Policy Versioning/Drafting: Iterate on policy changes in draft mode, then finalize to create immutable, versioned releases with full change history
  • Flexible Policy Text: Define custom prompt templates with content placeholders and configurable output fields

Key Concepts

What Changed

v1 Concept v2 Concept Description
Tag Labeling Endpoint A named configuration that groups policies for evaluation
Decision Label The result of evaluating content against a policy
PolicyVersion Policy + Version Number Policies are now referenced by key and version number

Terminology

Term Definition
Policy Key A human-readable string identifier for a policy (e.g., content-moderation), replacing the UUIDs used in v1
Labeler Used to label content based on some policy or other criteria
Content Key A named identifier on each content item (the key field) that maps it to an input defined in the policy.
Content Type The media type of a content item: TEXT or IMAGE
Content Format The encoding of the content data: PLAINTEXT for text, URL or BASE64 for images

Migration Overview

Migrating to v2 is a two-step process:

  1. Set up your v2 labeling endpoint — Create a labeling endpoint that references your policies. This replaces both v1 tags and direct policy version references.

  2. Update your API integration — Modify your API calls to use the new v2 endpoint URL and request/response formats.

Which Migration Path Applies to You?

Depending on how you're currently using the v1 API, follow the appropriate path in Step 2:

If you're using... Follow...
POST /policyai/v1/decisions/policies/{policyId}/versions/{policyVersionId} Migration Path 1: Single Policy Version Evaluation
POST /policyai/v1/decisions/evaluate/{tag_name} Migration Path 2: Tag-Based Evaluation

Both paths converge on the same v2 endpoint: /policyai/v2/labels/byEndpoint/{organizationId}/{endpointName}/

Note

You can label content directly against a policy without creating a Labeling Endpoint. However, we recommend using Labeling Endpoints, as they allow you evaluate content against multiple policies at once and swap policies in or out without changing your client code.


Step 1: Set Up Your v2 Labeling Endpoint

Before updating your API integration, you'll need to create a labeling endpoint in v2. This is required for both migration paths.

What is a Labeling Endpoint?

A labeling endpoint is a named, reusable configuration that defines:

  1. Which policies to run: Specified as a list of labelers
  2. Which versions to use: Each labeler specifies a policy key and version number

Endpoints serve the same purpose as v1 tags—grouping policies for evaluation—but with a simpler configuration model.

Creating an Endpoint

Endpoints can be created via the PolicyAI UI.

Navigate to the PolicyAI Manage Endpoints page. Click "Create New Endpoint," give your endpoint a descriptive name (e.g., dev, prod, staging), select the Policy Version you'd like to associate with the endpoint, and optionally add a description.

Note

The policies you've been using in v1 have already been ported to v2 by a member of the Musubi team. Simply create an endpoint associated with this policy, then you can use it to label content!

Best Practices

  1. Use descriptive names: Choose names that reflect the environment or purpose (e.g., prod-moderation, staging-full-suite)

  2. Separate environments: Create different endpoints for development, staging, and production

  3. Version control: When updating policies, test with a staging endpoint before updating production

Step 2: Update Your API Integration

Once your endpoint is set up, update your API calls. Choose the migration path that matches your current v1 integration.

Migration Path 1: Single Policy Version Evaluation

Migrating from: /policyai/v1/decisions/policies/{policyId}/versions/{policyVersionId}

Migrating to: /policyai/v2/labels/byEndpoint/{organizationId}/{endpointName}/

In v1, you could evaluate content against a specific policy version by providing the policy ID and version ID directly in the URL. In v2, you use the labeling endpoint you created in Step 1.

v1 Request

POST /policyai/v1/decisions/policies/{policyId}/versions/{policyVersionId}
Content-Type: application/json
Musubi-Api-Key: [your API key]
{
  "content": [
    {
      "type": "TEXT",
      "content": "Hello, I'm interested in your services. What are your rates?"
    }
  ]
}

v1 Response

{
  "messages": [],
  "data": [
    {
      "content": [
        {
          "type": "TEXT",
          "content": "Hello, I'm interested in your services. What are your rates?"
        }
      ],
      "createdTime": "2026-02-17T01:50:51.675552+00:00",
      "createdByUserId": "01961bd5-85b0-7d6e-9baa-1f62bc4f2803",
      "organizationId": "01975a9b-4b2c-7c76-bdce-3d38bf0b9725",
      "id": "019c694b-0b1b-7c02-adf0-b4f1b81c1504",
      "policyId": "019c6934-99ed-76a9-b4bd-c0194e292678",
      "policyVersionId": "019c6934-99ef-7ba0-924c-c9ed5fa43141",
      "policyVersionTagId": "019c693c-44c4-745f-a89a-f67d79598614",
      "modelId": 5,
      "assessment": "UNSAFE",
      "severity": 2,
      "category": "Selling",
      "reason": "The user is asking about rates for a service, indicating selling.",
      "extra": {
        "resultType": "TEXT",
        "promptFields": null
      },
      "messages": []
    }
  ]
}

v2 Request

POST /policyai/v2/labels/byEndpoint/{organizationId}/{endpointName}/
Content-Type: application/json
Musubi-Api-Key: [your API key]

Example URL: /policyai/v2/labels/byEndpoint/your-organization-uuid/my-content-policy/

{
  "content": [
    {
      "type": "TEXT",
      "format": "PLAINTEXT",
      "key": "content",
      "data": "Hello, I'm interested in your services. What are your rates?"
    }
  ]
}
Field Type Description
content array List of content items to label
content[].type string Content type: "TEXT" or "IMAGE"
content[].format string Format: "PLAINTEXT" for text, "URL" or "BASE64" for images
content[].key string A key that matches the name of the input that's been configured in your policy
content[].data string The actual content (text string, URL, or base64-encoded image)

v2 Response

{
  "messages": [],
  "data": {
    "modifiedTime": "2026-02-17T01:31:51.613406+00:00",
    "organizationId": "01975a9b-4b2c-7c76-bdce-3d38bf0b9725",
    "createdTime": "2026-02-17T01:31:51.613406+00:00",
    "id": "019c6939-a5bd-7103-850d-465ce0a25b90",
    "content": [
      {
        "type": "TEXT",
        "key": "content",
        "format": "PLAINTEXT",
        "data": "Hello, I'm interested in your services. What are your rates?",
        "messages": [],
        "sha256": "067c2c03ac4a76bef5b47c078404b1059d9d2ab75c2bc9d0ac7a6c60d03adad5",
        "redacted": false
      }
    ],
    "labels": [
      {
        "extra": {},
        "modifiedTime": "2026-02-17T01:31:51.613406+00:00",
        "createdTime": "2026-02-17T01:31:51.613406+00:00",
        "id": "019c6939-a5c2-7b68-83f7-7e7c46b83595",
        "status": "COMPLETED",
        "label": "Selling",
        "assessment": "FLAGGED",
        "labeler": {
          "policyKey": "defaultpolicy_a09d9433",
          "modelId": 100,
          "modelParamValues": {},
          "extraModelParamValues": {},
          "type": "POLICY",
          "versionNumber": 1
        },
        "labelerOutput": {
          "label": "Selling"
        },
        "contentKeys": [
          {
            "type": "TEXT",
            "key": "content"
          }
        ]
      }
    ],
    "endpointId": "019c6938-41d8-7279-8fe2-8f89f457593b"
  }
}

Note

The structure of the labelerOutput object depends on how your policy's custom outputs are defined - the only field guaranteed to be present is label.

Key Response Differences

v1 Field v2 Field Notes
assessment (SAFE/UNSAFE) assessment (CLEAR/FLAGGED) Renamed values
severity (0-3) labelerOutput.severityScore (0-3) Severity is now part of the labeler output object, if configured
category label The label returned by the policy
reason labelerOutput.reason Reason is now part of the labeler output object, if configured
policyVersionId labeler.policyKey + labeler.versionNumber Policy reference style changed

Additionally, any custom output fields defined on your policy will be included in the labelerOutput response.


Migration Path 2: Tag-Based Evaluation

Migrating from: /policyai/v1/decisions/evaluate/{tag_name}

Migrating to: /policyai/v2/labels/byEndpoint/{organizationId}/{endpointName}/

Understanding v1 Tags vs v2 Labeling Endpoints

In v1, tags were used to group multiple policy versions together for batch evaluation. You would:

  1. Create a tag in the UI
  2. Associate multiple policy versions with that tag
  3. Call the tag endpoint to evaluate content against all associated policies

In v2, labeling endpoints serve the same purpose:

  1. Create a Labeling Endpoint via the UI (see Step 1 above)
  2. Configure the list of labelers (policies + versions) directly in the endpoint
  3. Call the endpoint to label content against all configured policies
Aspect v1 Tags v2 Endpoints
Configuration Associate existing policy versions Define labelers with policy key + version
URL Structure /evaluate/{tag_name} /byEndpoint/{orgId}/{endpointName}/
Response Array of decisions Label group with array of labels

v1 Request

POST /policyai/v1/decisions/evaluate/{tag_name}
Content-Type: application/json
Musubi-Api-Key: [your API key]

Example URL: /policyai/v1/decisions/evaluate/production

{
  "content": [
    {
      "type": "TEXT",
      "content": "Check out my profile for special offers!"
    }
  ]
}

v1 Response

{
  "messages": [],
  "data": [
    {
      "content": [
        {
          "type": "TEXT",
          "content": "Check out my profile for special offers!"
        }
      ],
      "createdTime": "2026-02-17T01:36:30.433656+00:00",
      "createdByUserId": "01961bd5-85b0-7d6e-9baa-1f62bc4f2803",
      "organizationId": "01975a9b-4b2c-7c76-bdce-3d38bf0b9725",
      "id": "019c693d-e6e0-7b3f-999d-1099cfeba876",
      "policyId": "019c6934-99ed-76a9-b4bd-c0194e292678",
      "policyVersionId": "019c6934-99ef-7ba0-924c-c9ed5fa43141",
      "policyVersionTagId": "019c693c-44c4-745f-a89a-f67d79598614",
      "modelId": 5,
      "assessment": "UNSAFE",
      "severity": 1,
      "category": "Selling",
      "reason": "The content promotes special offers, which falls under selling.",
      "extra": {
        "resultType": "TEXT",
        "promptFields": null
      },
      "messages": []
    }
  ]
}

v2 Request

POST /policyai/v2/labels/byEndpoint/{organizationId}/{endpointName}/
Content-Type: application/json
Musubi-Api-Key: [your API key]

Example URL: /policyai/v2/labels/byEndpoint/your-organization-uuid/production/

{
  "content": [
    {
      "type": "TEXT",
      "format": "PLAINTEXT",
      "key": "message",
      "data": "Check out my profile for special offers!"
    }
  ]
}

v2 Response

{
  "messages": [],
  "data": {
    "modifiedTime": "2026-02-17T01:38:57.997848+00:00",
    "organizationId": "01975a9b-4b2c-7c76-bdce-3d38bf0b9725",
    "createdTime": "2026-02-17T01:38:57.997848+00:00",
    "id": "019c6940-274d-7ca6-b5be-42607a141cfc",
    "content": [
      {
        "type": "TEXT",
        "key": "content",
        "format": "PLAINTEXT",
        "data": "Check out my profile for special offers!",
        "messages": [],
        "sha256": "31319ddf797178fd79529416405d0380e4be93c60239816695d7be072df0c8a3",
        "redacted": false
      }
    ],
    "labels": [
      {
        "extra": {},
        "modifiedTime": "2026-02-17T01:38:57.997848+00:00",
        "createdTime": "2026-02-17T01:38:57.997848+00:00",
        "id": "019c6940-2751-75d9-b305-62f48f8398e5",
        "status": "COMPLETED",
        "label": "Selling",
        "assessment": "FLAGGED",
        "labeler": {
          "policyKey": "defaultpolicy_a09d9433",
          "modelId": 100,
          "modelParamValues": {},
          "extraModelParamValues": {},
          "type": "POLICY",
          "versionNumber": 1
        },
        "labelerOutput": {
          "label": "Selling"
        },
        "contentKeys": [
          {
            "type": "TEXT",
            "key": "content"
          }
        ]
      }
    ],
    "endpointId": "019c6938-41d8-7279-8fe2-8f89f457593b"
  }
}

Reference: Content Format Changes

v1 Content Format

{
  "content": [
    {
      "type": "TEXT",
      "content": "Your text here"
    }
  ]
}

v2 Content Format

{
  "content": [
    {
      "type": "TEXT",
      "format": "PLAINTEXT",
      "key": "content",
      "data": "Your text here"
    }
  ]
}

Content Types Reference

Content Type Format Example
Text type: "TEXT", format: "PLAINTEXT" "data": "Hello world"
Image URL type: "IMAGE", format: "URL" "data": "https://example.com/image.jpg"
Image Base64 type: "IMAGE", format: "BASE64" "data": "iVBORw0KGgo..."

Content Keys

The key field in v2 content maps to the content keys defined in your policies.


Reference: Assessment Value Changes

v1 Assessment v2 Assessment Meaning
SAFE CLEAR No policy violation detected
UNSAFE FLAGGED Policy violation detected

Migration Checklist

  • Identify all v1 API calls in your codebase
  • Create v2 endpoint(s) corresponding to your v1 tags/policies (see Step 1)
  • Update content payload format (add format, key, rename content to data)
  • Update response parsing to handle new structure (labels array, labelerOutput)
  • Update assessment value checks (SAFECLEAR, UNSAFEFLAGGED)
  • Test thoroughly in staging before switching production traffic

Need Help?

If you have further questions about migrating to the v2 API, please contact a member of our team or refer to the API documentation.