Skip to main content

API Reference

This guide provides comprehensive documentation for the Freeplay HTTP API, including authentication, available endpoints, request/response formats, and code examples.

Authentication

Your API root is the URL from your Freeplay instance plus the suffix /api/v2/. For example: https://app.freeplay.ai/api/v2

Custom Domain

If you are a private deployment, your API root may be something like https://acme.freeplay.ai/api/v2.

API Keys

Freeplay authenticates your API requests using your API Key, which can be managed through the Freeplay application at https://app.freeplay.ai/settings/api-access. To authenticate, provide an API key in the Authorization header:
Authorization: Bearer {freeplay_api_key}

API Errors

When using the Freeplay HTTP API, you may encounter various error responses. We use standard HTTP error codes: 200 for success, 400 for bad requests, 401 for authorization errors, and 500 for server errors. Most errors include an HTTP body in JSON format describing what went wrong and how to fix the issue.
Do not retry errors with codes in the 400-499 range, as they are typically client errors that will not be fixed by retrying.
For 500 errors, retry the request up to three times with at least a 5-second delay between attempts. These are often transient issues that may resolve on retry.

Common Error Codes

400 Bad Request

The request was malformed or contained invalid data. This occurs when the request is not proper JSON or contains invalid field values.
{
  "message": "Session ID 456 is not a valid uuid4 format."
}

401 Unauthorized

The API request does not contain valid bearer authentication with an API key.
{
  "message": "Requests must be authenticated with a valid API key."
}

404 Not Found

One of the IDs passed in the request is invalid or the object does not exist. This can happen if the referenced object was deleted or if the caller does not have access to the object.
{
  "message": "Project not found"
}

500 Server Error

An error occurred in the Freeplay server. Our team is automatically notified. These are often transient issues that can be resolved by retrying the request after a delay.
{
  "message": "An unexpected error occurred. Please try again later."
}
When encountering errors, verify your request parameters and payload match the API specifications. If issues persist, contact Freeplay support for assistance.

Projects

A project in Freeplay represents a meaningful separation between your AI products. Most commonly, teams organize projects by AI product for easier tracking.

List All Projects

GET /projects/all Retrieves all projects in your Freeplay workspace.
  • This endpoint does not work with project-scoped API keys - Private projects are not returned
curl --location 'https://app.freeplay.ai/api/v2/projects/all' \
  --header 'Authorization: Bearer <APIKEY>'
Sample Response
{
  "projects": [
    {
      "id": "<id>",
      "name": "FreeplayChat"
    },
    {
      "id": "<id>",
      "name": "Freeplay Support Agent"
    },
    {
      "id": "<id>",
      "name": "Getting Started Project"
    }
  ]
}

Prompt Templates

A prompt template in the Freeplay platform is a pre-defined structure for creating prompts that are used to interact with LLMs. Base URL: /api/v2/projects/<project-id>/prompt-templates

Retrieve a Prompt Template by Name

POST /name/<template-name> You can retrieve a prompt template in one of three forms:
  • Raw: Returns the prompt template without input variable values inserted
  • Bound: Returns the prompt template with input variable values inserted in a consistent format. Pass your input variables in the request body.
  • Formatted (most common): Returns the prompt template with input variable values inserted and content formatted according to your LLM provider. Pass format=true as a query parameter and pass your input variables in the request body.
Query Parameters
ParameterTypeDescriptionRequired
environmentstrThe environment tag of the prompt you want to fetchNo (Default: latest)
formatbooleanIndicates whether to return a Formatted or Bound PromptNo

Retrieve a Formatted Prompt

curl 'https://app.freeplay.ai/api/v2/projects/8f93dd00-2eb5-4ba2-9354-86d5c6831dfd/prompt-templates/name/album_bot?environment=prod&format=true' \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer TOKEN' \
  --data '{
    "pop_star": "justin beiber"
  }'
Sample Response
{
  "format_version": 2,
  "formatted_content": [
    {
      "content": "You are a helpful bot that generates album names\nGenerate a two word album name in the style of justin beiber",
      "role": "user"
    }
  ],
  "formatted_tool_schema": [
    {
      "function": {
        "description": "Get weather of a location",
        "name": "weather_of_location",
        "parameters": {
          "properties": {
            "location": {
              "description": "Location to get the weather for",
              "type": "string"
            }
          },
          "required": ["location"],
          "type": "object"
        }
      },
      "type": "function"
    }
  ],
  "metadata": {
    "flavor": "openai_chat",
    "model": "gpt-3.5-turbo-0125",
    "params": {
      "max_tokens": 100,
      "temperature": 0.2
    },
    "provider": "openai",
    "provider_info": {}
  },
  "prompt_template_id": "5cccc8e6-b163-4094-8bd2-90030f151ec8",
  "prompt_template_name": "album_bot",
  "prompt_template_version_id": "f503c15e-2f0f-4ce4-b443-4c87d0b6435d",
  "system_content": null
}

Retrieve a Bound Prompt

curl 'https://app.freeplay.ai/api/v2/projects/8f93dd00-2eb5-4ba2-9354-86d5c6831dfd/prompt-templates/name/album_bot?environment=prod' \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer TOKEN' \
  --data '{
    "pop_star": "justin beiber"
  }'
Sample Response
{
  "bound_content": [
    {
      "content": "You are a helpful bot that generates album names\nGenerate a two word album name in the style of justin beiber",
      "role": "user"
    }
  ],
  "format_version": 2,
  "metadata": {
    "flavor": "openai_chat",
    "model": "gpt-3.5-turbo-0125",
    "params": {
      "max_tokens": 100,
      "temperature": 0.2
    },
    "provider": "openai",
    "provider_info": {}
  },
  "prompt_template_id": "5cccc8e6-b163-4094-8bd2-90030f151ec8",
  "prompt_template_name": "album_bot",
  "prompt_template_version_id": "f503c15e-2f0f-4ce4-b443-4c87d0b6435d",
  "tool_schema": [
    {
      "description": "Get weather of a location",
      "name": "weather_of_location",
      "parameters": {
        "additionalProperties": false,
        "properties": {
          "location": {
            "description": "Location to get the weather for",
            "type": "string"
          }
        },
        "required": ["location"],
        "type": "object"
      }
    }
  ]
}

Retrieve a Raw Prompt Template

curl 'https://app.freeplay.ai/api/v2/projects/8f93dd00-2eb5-4ba2-9354-86d5c6831dfd/prompt-templates/name/album_bot?environment=prod' \
  --header 'Authorization: Bearer TOKEN'
Sample Response
{
  "content": [
    {
      "content": "You are a helpful bot that generates album names\nGenerate a two word album name in the style of {{pop_star}}",
      "role": "user"
    }
  ],
  "format_version": 2,
  "metadata": {
    "flavor": "openai_chat",
    "model": "gpt-3.5-turbo-0125",
    "params": {
      "max_tokens": 100,
      "temperature": 0.2
    },
    "provider": "openai",
    "provider_info": {}
  },
  "prompt_template_id": "5cccc8e6-b163-4094-8bd2-90030f151ec8",
  "prompt_template_name": "album_bot",
  "prompt_template_version_id": "f503c15e-2f0f-4ce4-b443-4c87d0b6435d",
  "tool_schema": [
    {
      "description": "Get weather of a location",
      "name": "weather_of_location",
      "parameters": {
        "additionalProperties": false,
        "properties": {
          "location": {
            "description": "Location to get the weather for",
            "type": "string"
          }
        },
        "required": ["location"],
        "type": "object"
      }
    }
  ]
}

Retrieve All Prompt Templates in an Environment

GET /all/<environment-name>
curl 'https://app.freeplay.ai/api/v2/projects/<project-id>/prompt-templates/all/latest' \
  --header 'Authorization: Bearer TOKEN'
Sample Response
{
  "prompt_templates": [
    {
      "content": [
        {
          "content": "You are a helpful bot that generates album names\nRespond with only the album name",
          "role": "system"
        },
        {
          "content": "Generate a two word album name in the style of {{pop_star}}",
          "role": "user"
        }
      ],
      "format_version": 2,
      "metadata": {
        "flavor": "anthropic_chat",
        "model": "claude-3-opus-20240229",
        "params": {
          "max_tokens": 256,
          "temperature": 0.5
        },
        "provider": "anthropic",
        "provider_info": {}
      },
      "prompt_template_id": "5cccc8e6-b163-4094-8bd2-90030f151ec8",
      "prompt_template_name": "album_bot",
      "prompt_template_version_id": "eb934c9f-ee1e-42be-a9cb-d193f5d3aa54"
    },
    {
      "content": [
        {
          "content": "You are a news article summarization agent. Your job is to find the big idea in an article, distill it into compelling content & write a catchy summary that people want to click. You will be provided with the article title, source url and the article text\nThe scraped source content of the URL for the article is provided. ",
          "role": "system"
        },
        {
          "content": "Here is the scraped content from the web page:\n--------\n{{article_title}}\n{{article_text}}\n{{source_url}}\n--------\n\nSome additional instructions follow. Please follow these carefully.\n\nThere may be user comments at the bottom or other superfluous information included from the scraped content. Ignore those and focus on the body of the article. \n\nAt the end of each response, after a newline always include the original URL for the article so people can read the article. \n\nWhen sharing the URL, include it after two line breaks and use this format starting with \"Read more\" and stripping the http:// at the start of the url: Read more: www.example.com\nGiven this article, respond with a Tweet of no more than 200 characters. Never include hashtags. A relevant emoji is ok. Do not include quotes around any content.\n\nAfter a newline, include the original URL for the article so people can read the article.",
          "role": "user"
        }
      ],
      "format_version": 2,
      "metadata": {
        "flavor": "openai_chat",
        "model": "gpt-3.5-turbo-16k-0613",
        "params": {
          "max_tokens": 2000,
          "temperature": 0.5
        },
        "provider": "openai",
        "provider_info": {}
      },
      "prompt_template_id": "9daa7233-16d3-48b3-a739-ddba66742b08",
      "prompt_template_name": "article_summarizer",
      "prompt_template_version_id": "9f9f9bfe-ed53-449d-a13c-5f88198c0f8e"
    }
  ]
}

Retrieve a Prompt Template by Version ID

POST /id/<template-id>/versions/<template-version-id> Retrieves a prompt template using a specific version ID. This is useful when you want to pin your code to a specific prompt version regardless of actions taken in the Freeplay app. The same Raw, Bound, and Formatted options exist when retrieving by version ID as they do when retrieving by name.
curl 'https://app.freeplay.ai/api/v2/projects/8f93dd00-2eb5-4ba2-9354-86d5c6831dfd/prompt-templates/id/0053c449-c736-4557-a3d4-bf3b852d0604/versions/42d301b2-cdc3-48d5-829b-b892561017ff' \
  --header 'Authorization: Bearer TOKEN'
Sample Response
{
  "content": [
    {
      "content": "You are a technical customer support agent for a product called freeplay. Your job is to help users answer questions about freeplay.\nYou will often be provided with information that will help you answer the question. If the information is relevant then use it to answer the question and end your response with \"See <source> for more details\" where <source> is a valid HTML formatted hyperlink to the source URL.\n\nIf the information provided is not relevant then ignore it.\n\nUser Query: {{question}}\n----\nSupporting Information: {{supporting_information}}",
      "role": "user"
    }
  ],
  "format_version": 2,
  "metadata": {
    "flavor": "openai_chat",
    "model": "gpt-3.5-turbo-16k-0613",
    "params": {
      "max_tokens": 2000,
      "temperature": 0.5
    },
    "provider": "openai",
    "provider_info": {}
  },
  "prompt_template_id": "0053c449-c736-4557-a3d4-bf3b852d0604",
  "prompt_template_name": "rag-qa",
  "prompt_template_version_id": "42d301b2-cdc3-48d5-829b-b892561017ff"
}

Create a Prompt Template Version

POST /id/<template-id>/versions Adds a new version to an existing prompt template. The new version is immediately deployed to the latest environment unless you specify different environments. Parameters
ParameterTypeDescriptionRequired
contentstrJSON-encoded array of chat-style messages, each with role and content. Variables use {{variable}} syntax.Yes
modelstrModel name (e.g. gpt-4o-2024-05-13)Yes
providerstrProvider key (openai, anthropic, vertex, etc.)Yes
llm_parametersdict[str, number, bool]Overrides for model parameters (e.g. temperature, max_tokens)Yes
tool_schemalist[NormalizedToolSchema]JSON schema(s) for function/tool calls. Each name must be unique.No
version_namestrHuman-readable name shown in the UINo
version_descriptionstrLonger description shown in the UINo
environmentslist[str]Deploy this version to the specified environments (defaults to ["latest"])No
Successful calls return 201 Created with the new version’s metadata.

Example Request

curl -X POST \
  'https://app.freeplay.ai/api/v2/projects/9cf924e8-4474-442a-bf9f-53f8415a4bd2/prompt-templates/id/d500b46e-0ae2-46c0-ac0e-0c18feb180fb/versions' \
  --header 'Authorization: Bearer TOKEN' \
  --header 'Content-Type: application/json' \
  --data '{
    "content": "[{\"role\": \"system\", \"content\": \"You are a helpful assistant.\"}, {\"role\": \"user\", \"content\": \"Hello {{name}}!\"}]",
    "provider": "openai",
    "model": "gpt-4o-2024-05-13",
    "llm_parameters": {
      "temperature": 0.2,
      "max_tokens": 256
    },
    "tool_schema": [
      {
        "name": "get_weather",
        "description": "Get weather information",
        "parameters": {
          "type": "object",
          "properties": {
            "location": {
              "type": "string"
            }
          },
          "required": ["location"]
        }
      }
    ],
    "version_name": "v2 – temp 0.2",
    "version_description": "Updated version with lower temperature",
    "tags": ["latest"]
  }'
Sample Response (201)
{
  "content": [
    {
      "content": "You are a helpful assistant.",
      "role": "system"
    },
    {
      "content": "Hello {{name}}!",
      "role": "user"
    }
  ],
  "created_at": 1754585113,
  "format_version": 3,
  "metadata": {
    "flavor": "openai_chat",
    "model": "gpt-4o-2024-05-13",
    "params": {
      "max_tokens": 256,
      "temperature": 0.2
    },
    "provider": "openai",
    "provider_info": {}
  },
  "project_id": "9cf924e8-4474-442a-bf9f-53f8415a4bd2",
  "prompt_template_id": "d500b46e-0ae2-46c0-ac0e-0c18feb180fb",
  "prompt_template_name": "testing234",
  "prompt_template_version_id": "3a583bb3-901e-4f78-8364-f23b2f260019",
  "tool_schema": [
    {
      "description": "Get weather information",
      "name": "get_weather",
      "parameters": {
        "properties": {
          "location": {
            "type": "string"
          }
        },
        "required": ["location"],
        "type": "object"
      }
    }
  ],
  "version_description": "Updated version with lower temperature",
  "version_name": "v2 – temp 0.2"
}

Validation & Errors

400 Bad Request
  • "Invalid prompt template content format …"content is not valid JSON or message list
  • "No matching model found …" – unknown model/provider
  • "This provider does not support tool calls …" – model lacks tool-use support
  • "Duplicate tool name found …" – duplicate names in tool_schema
404 Not Found
  • template_id does not exist or you lack access

Update Environments for a Prompt Template Version

POST /id/<template-id>/versions/<version-id>/environments Assigns an existing prompt template version to one or more environments.
  • Environments must already exist in your account - The latest environment is managed by the system and is ignored if provided - This operation is idempotent and additive: it ensures the version is tagged with the provided environments but does not remove any previously assigned environments
Parameters
ParameterTypeDescriptionRequired
environmentslist[string]Environment names to assign (e.g., ["staging", "prod"]). latest is ignored.Yes
curl -X POST \
  'https://app.freeplay.ai/api/v2/projects/8f93dd00-2eb5-4ba2-9354-86d5c6831dfd/prompt-templates/id/0053c449-c736-4557-a3d4-bf3b852d0604/versions/42d301b2-cdc3-48d5-829b-b892561017ff/environments' \
  --header 'Authorization: Bearer TOKEN' \
  --header 'Content-Type: application/json' \
  --data '{"environments": ["staging", "prod", "latest"]}'
Sample Response (200)
{}

Validation & Errors

400 Bad Request
  • "Environment '<name>' not found …" – the environment does not exist for your account
404 Not Found
  • The template_id or version_id does not exist, or you lack access

Agents

An agent in Freeplay is a process in your application that makes use of multiple LLM calls to generate a single response. Base URL: /api/v2/projects/<project-id>/agents

List Agents

GET / Returns agents in descending chronological order (newest first). Use pagination to navigate beyond the initial page. Parameters
ParameterTypeDescriptionRequired
pageintThe page number of agents to retrieveNo (Default: 1)
page_sizeintThe number of agents to include in the response (maximum: 100)No (Default: 30)
namestringFilter agents by exact name match (case-sensitive)No
curl --location 'https://app.freeplay.ai/api/v2/projects/8f93dd00-2eb5-4ba2-9354-86d5c6831dfd/agents' \
  --header 'Authorization: Bearer TOKEN'
Sample Response
{
  "data": [
    {
      "id": "deadbeef-dead-0000-beef-deadbeefdead",
      "name": "customer_support_agent"
    },
    {
      "id": "f00dbabe-f00d-1111-babe-f00dbabef00d",
      "name": "sales_assistant"
    }
  ],
  "pagination": {
    "page": 1,
    "page_size": 30,
    "has_next": false
  }
}
Example with Filters
# Get agents with a specific name
curl --location 'https://app.freeplay.ai/api/v2/projects/8f93dd00-2eb5-4ba2-9354-86d5c6831dfd/agents?name=customer_support_agent' \
  --header 'Authorization: Bearer TOKEN'

# Get second page of agents with custom page size
curl --location 'https://app.freeplay.ai/api/v2/projects/8f93dd00-2eb5-4ba2-9354-86d5c6831dfd/agents?page=2&page_size=50' \
  --header 'Authorization: Bearer TOKEN'

Sessions

Sessions are a collection of one or more LLM completions, enabling you to tie completions together in whatever logical grouping makes sense for your application (multi-turn chat, chains, agent flows, etc.). Base URL: /api/v2/projects/<project-id>/sessions

Record a Completion

POST /<session-id>/completions Records a completion to a session. This endpoint allows you to send LLM interaction data to Freeplay for observability and analysis. Request Payload
ParameterTypeDescriptionRequired
messageslist[{"role": Literal["user", "assistant"], "content": str}]List of messages sent to the LLM. Note for Anthropic: messages must exclude message fields other than role and content.Yes
tool_schemalist[{name: str, description: str, parameters: dict[str, any]}]Tool schema used with the completionNo
inputsdict[str, any]Input variables used to build the formatted prompt. Values must not be null.Yes
session_info{custom_metadata: dict[str, str]}Information to be associated with the sessionNo
prompt_info{prompt_template_version_id: UUID, environment: str}Information associated with the prompt templateYes
call_info{start_time: float, end_time: float, model: str, provider: str, provider_info: dict[str, any], llm_parameters: dict[str, any], usage: {prompt_tokens: int, completion_tokens: int}, api_style: 'batch' or 'default'}Information associated with the LLM call. Note: If usage is included, the token count will be used directly to calculate costs. If empty, Freeplay will estimate token counts using Tiktoken.No
test_run_info{test_run_id: UUID, test_case_id: UUID}Information for the associated test runNo
completion_idUUIDThe UUIDv4 that should be used as the ID for the completion. Valid for sending completion feedback.No
eval_resultsdict[str, bool or float]Code evaluations for a completion. These will be displayed alongside model-graded evals and human labels for a completion.No
curl 'https://app.freeplay.ai/api/v2/projects/8f93dd00-2eb5-4ba2-9354-86d5c6831dfd/sessions/f503c15e-2f0f-4ce4-b443-4c87d0b6435d/completions' \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer TOKEN' \
  --data '{
    "messages": [
      {
        "content": "You are a helpful bot that generates album names\nGenerate a two word album name in the style of Taylor Swift",
        "role": "user"
      },
      {
        "role": "assistant",
        "content": "\"Rainy Melodies\""
      }
    ],
    "inputs": {
      "pop_star": "Taylor Swift"
    },
    "prompt_info": {
      "prompt_template_version_id": "f503c15e-2f0f-4ce4-b443-4c87d0b6435d",
      "environment": "prod"
    },
    "eval_results": {
      "includes_required_keyword": true,
      "toxicity_score": 0.28
    }
  }'
Sample Response
{
  "completion_id": "707bc301-85e9-4f02-aa97-faba8cd7774a"
}
Record an LLM Interaction End to End
import requests
from dotenv import load_dotenv
import os
import json
import uuid

load_dotenv("../.env")
project_id = os.getenv("FREEPLAY_PROJECT_ID")
freeplay_api_root = f"https://app.freeplay.ai/api/v2/projects/{project_id}"
openai_api_url = "https://api.openai.com/v1/chat/completions"

## PROMPT ##
# fetch the prompt
prompt_name = "album_bot"
input_variables = {"pop_star": "Taylor Swift"}
prompt_req = requests.post(
    url=f"{freeplay_api_root}/prompt-templates/name/{prompt_name}",
    headers={
        "Authorization" : f"Bearer {os.getenv('FREEPLAY_KEY')}"
    },
    params={
        "environment": "prod",
        "format": "true"
    },
    data=json.dumps(input_variables)
)
formatted_prompt = prompt_req.json()

## LLM ##
completion_payload = {
        "model": formatted_prompt["metadata"]["model"],
        "messages": formatted_prompt["formatted_content"],
        **formatted_prompt["metadata"]["params"]
}

completion_req = requests.post(
    url=openai_api_url,
    headers={
        "Authorization" : f"Bearer {os.getenv('OPENAI_API_KEY')}",
        "Content-Type": "application/json"
    },
    data=json.dumps(completion_payload)
)
response_message = completion_req.json()['choices'][0]['message']

## RECORD ##
# record the interaction to freeplay
# add the LLM response to the message queue
messages = formatted_prompt["formatted_content"]
messages.append(response_message)

# construct the payload
record_payload = {
    "messages": messages,
    "inputs": input_variables,
    "prompt_info": {
        "prompt_template_version_id": formatted_prompt["prompt_template_version_id"],
        "environment": "prod"
    }
}

# create a session id
session_id = str(uuid.uuid4())

# record to freeplay
requests.post(
    url=f"{freeplay_api_root}/sessions/{session_id}/completions",
    headers={
        "Authorization": f"Bearer {os.getenv('FREEPLAY_KEY')}",
        "Content-Type": "application/json"
    },
    data = json.dumps(record_payload)
)

Retrieve Existing Sessions/Completions

GET / Retrieves sessions, including their metadata and completions, starting from most recent first. Parameters
ParameterTypeDescriptionRequired
pageintThe page number of sessions to retrieveNo (Default: 1)
page_sizeintThe number of sessions to include in the request (maximum: 100)No (Default: 10)
from_datestrEarliest date for which sessions will be returned (inclusive). Format: YYYY-MM-DDNo
to_datestrLatest date for which sessions will be returned (exclusive). Format: YYYY-MM-DDNo
test_liststrFilters to sessions in the given dataset. Pass in the name of the dataset.No
test_run_idstrFilters to sessions in the given test runNo
prompt_namestrFilters to sessions associated with the given prompt nameNo
review_queue_idstrFilters to sessions in the given review queueNo
custom_metadata.*dictFilters to sessions that have the associated metadata value at the session levelNo
curl --header "Authorization: Bearer $FREEPLAY_API_KEY" \
  "https://app.freeplay.ai/api/v2/projects/$FREEPLAY_PROJECT_ID/sessions"
Sample Response
[
  {
    "custom_metadata": {},
    "messages": [
      {
        "client_evaluation_results": [],
        "completion_id": "0202ae57-1098-4d3e-94f7-1fbb034fbed5",
        "customer_feedback": {},
        "environment": "latest",
        "evaluation_results": [],
        "format_version": 3,
        "input_variables": {
          "question": "How do I bundle prompts so my system is reliable?"
        },
        "message_type": "completion",
        "model_name": "claude-2.1",
        "prompt": [
          {
            "content": "You are a technical customer support agent for a product called freeplay.",
            "role": "system"
          },
          {
            "content": "How do I bundle prompts so my system is reliable?",
            "role": "user"
          }
        ],
        "prompt_template_name": "my-prompt",
        "provider_name": "anthropic",
        "response": "You bundle prompts by downloading them with the python SDK, packaging them with your application, and using the FilesystemTemplateResolver when using the Freeplay SDK.",
        "trace_id": null
      }
    ],
    "session_id": "85d7d393-4e85-4664-8598-5dd91dc75b5b",
    "start_time": "2024-07-05T14:33:02.721000"
  }
]

Additional Filtering Examples

The following example demonstrates using multiple filters:
  • from_date: 2025-08-01
  • to_date: 2025-08-22
  • page: 1
  • page_size: 70 (default is 10, max is 100)
  • custom_metadata.is_llm_only: false
curl --header "Authorization: Bearer $FREEPLAY_API_KEY" \
  --location 'https://app.freeplay.ai/api/v2/projects/$FREEPLAY_PROJECT_ID/sessions?from_date=2025-08-01&to_date=2025-08-22&page=1&page_size=70&custom_metadata.is_llm_only=false'

Delete a Session

DELETE /api/v2/projects/<project-id>/sessions/<session-id> Deletes a session and all associated completions.
Deleting a session is permanent and cannot be undone.
curl --request DELETE \
  'https://app.freeplay.ai/api/v2/projects/8f93dd00-2eb5-4ba2-9354-86d5c6831dfd/sessions/a903c15e-2f0f-4ce4-b743-4c87d0c64918' \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer TOKEN'

Test Runs

Test runs in Freeplay provide a structured way to generate batch tests for your language models using saved datasets managed on the Freeplay server. Base URL: /api/v2/projects/<project-id>/test-runs

Create a Test Run

POST / Creates a new test run from an existing dataset. Request Payload
ParameterTypeDescriptionRequired
dataset_namestrThe name of the dataset for which you want to create a test runYes
include_outputsbooleanDetermines if outputs are returned for test casesNo (Default: true)
test_run_namestrName for the test run, to be displayed in the Freeplay appNo
test_run_descriptionstrDescription for the test run, to be displayed in the Freeplay appNo
curl 'https://app.freeplay.ai/api/v2/projects/<project-id>/test-runs' \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer TOKEN' \
  --data '{
    "dataset_name": "Example Tests"
  }'
Sample Response
{
  "test_cases": [
    {
      "output": null,
      "test_case_id": "91e60c9e-fbaa-4990-b4cc-7a8bd067f298",
      "variables": {
        "question": "How do test runs relate to test cases?",
        "supporting_information": "([SearchChunk(title='Test Runs', link='https://docs.freeplay.ai/docs/test-runs-with-freeplay', content='Test Runs')..."
      }
    },
    {
      "output": null,
      "test_case_id": "c45dbc01-a177-449b-ab66-bb5a469748fe",
      "variables": {
        "question": "Write me some typescript code to run a new Test Run in freeplay",
        "supporting_information": "[{\"id\":88,\"version\":5,\"score\":0.8668302,\"payload\":{\"description\":\"Ship better products with LLMs...."
      }
    }
  ],
  "test_run_description": "",
  "test_run_id": "bd3eb06c-f93b-46a4-aa3b-d240789c8a06",
  "test_run_name": ""
}
Execute a Test Run end to end
import requests
from dotenv import load_dotenv
import os
import json
import uuid

load_dotenv("../.env")
project_id = "45e20940-322a-4cb8-810f-3934a56aa5e2"
freeplay_api_root = f"https://app.freeplay.ai/api/v2/projects/{project_id}"
openai_api_url = "https://api.openai.com/v1/chat/completions"

# create the test run
test_run_req = requests.post(
    url= freeplay_api_root + "/test-runs",
    headers={
        "Authorization": f"Bearer {os.getenv('FREEPLAY_KEY')}",
        "Content-Type": "application/json"
    },
    data=json.dumps({
        "dataset_name": "Example Tests"
    })
)
test_run = test_run_req.json()

prompt_name = "rag-qa"
# loop over each test case in the dataset
for test_case in test_run["test_cases"]:
    # fetch the formatted prompt
    prompt_req = requests.post(
        url=f"{freeplay_api_root}/prompt-templates/name/{prompt_name}",
        headers={
            "Authorization" : f"Bearer {os.getenv('FREEPLAY_KEY')}"
        },
        params={
            "environment": "prod",
            "format": "true"
        },
        data=json.dumps(test_case['variables'])
    )
    formatted_prompt = prompt_req.json()

    # execute the completion
    completion_payload = {
        "model": formatted_prompt["metadata"]["model"],
        "messages": formatted_prompt["formatted_content"],
        **formatted_prompt["metadata"]["params"]
    }
    completion_req = requests.post(
        url=openai_api_url,
        headers={
            "Authorization" : f"Bearer {os.getenv('OPENAI_API_KEY')}",
            "Content-Type": "application/json"
        },
        data=json.dumps(completion_payload)
    )
    response_message = completion_req.json()['choices'][0]['message']

    # record to freeplay
    messages = formatted_prompt["formatted_content"]
    messages.append(response_message)

    record_payload = {
        "messages": messages,
        "inputs": test_case['variables'],
        "prompt_info": {
            "prompt_template_version_id": formatted_prompt["prompt_template_version_id"],
            "environment": "prod"
        },
        # include the test run info to link the session to the test run
        "test_run_info": {
            "test_run_id": test_run["test_run_id"],
            "test_case_id": test_case["test_case_id"]
        }
    }

    # create a session
    session_id = str(uuid.uuid4())
    record_res = requests.post(
        url=f"{freeplay_api_root}/sessions/{session_id}/completions",
        headers={
            "Authorization": f"Bearer {os.getenv('FREEPLAY_KEY')}",
            "Content-Type": "application/json"
        },
        data=json.dumps(record_payload)
    )

Retrieve Test Run Results

GET /id/<test-run-id>
curl 'https://app.freeplay.ai/api/v2/projects/8f93dd00-2eb5-4ba2-9354-86d5c6831dfd/test-runs/id/2a9dd8bd-6c29-47c8-9ca4-427f73174881' \
  --header 'Authorization: Bearer TOKEN'
Sample Response
{
  "created_at": 1733770879,
  "description": "",
  "id": "2a9dd8bd-6c29-47c8-9ca4-427f73174881",
  "model_name": "gpt-4o-mini-2024-07-18",
  "name": "132--after-concurrency",
  "prompt_name": "rag-qa",
  "prompt_version": "gpt-4o-mini",
  "sessions_count": 132,
  "summary_statistics": {
    "auto_evaluation": {
      "Answer Accuracy": {
        "1": 104,
        "2": 9,
        "4": 9,
        "5": 10
      },
      "Answer Faithfulness": {
        "no": 126,
        "yes": 6
      },
      "Context Relevance": {
        "1": 116,
        "4": 2,
        "5": 14
      }
    },
    "client_evaluation": {},
    "human_evaluation": {}
  }
}

List Test Runs

GET / Returns test runs in descending chronological order. Use pagination to navigate beyond the most recent 100. Parameters
ParameterTypeDescriptionRequired
pageintThe page number of test runs to retrieve (maximum page size: 100)No (Default: 1)
page_sizeintThe number of test runs to include in the requestNo (Default: 100)
curl --location 'https://app.freeplay.ai/api/v2/projects/45e20940-322a-4cb8-810f-3934a56aa5e2/test-runs' \
  --header 'Authorization: Bearer TOKEN'
Sample Response
{
  "test_runs": [
    {
      "created_at": 1733770879,
      "dataset_name": "big-dataset",
      "description": "",
      "id": "2a9dd8bd-6c29-47c8-9ca4-427f73174881",
      "name": "132--after-concurrency"
    },
    {
      "created_at": 1732651609,
      "dataset_name": "Alec's Demo",
      "description": "",
      "id": "ab869ac0-e688-4abf-864d-cbce8dbbb1c5",
      "name": "test"
    },
    {
      "created_at": 1731966113,
      "dataset_name": "big-dataset",
      "description": "",
      "id": "64faf9fd-b263-4477-acfc-18f576befe4d",
      "name": "big-test"
    }
  ]
}

Customer Feedback

Completion Feedback

Completion feedback allows you to submit feedback for a specific completion in your project. Base URL: /api/v2/projects/<project-id>/completion-feedback

Create Completion Feedback

POST /id/<completion_id> Submits feedback for a completion, which can be used to track user satisfaction and improve model performance. Request Payload
ParameterTypeDescriptionRequired
feedback_attributestr / float / int / boolKey-value pairs of feedback attributesYes
freeplay_feedbackstrValue must be either "positive" or "negative"Yes
Example Request
curl 'https://app.freeplay.ai/api/v2/projects/<project-id>/completion-feedback/id/707bc301-85e9-4f02-aa97-faba8cd7774a' \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer TOKEN' \
  --data '{
    "feedback_attribute_1": "value",
    "feedback_attribute_2": 10,
    "freeplay_feedback": "positive"
  }'
Sample Response Payload:
{
  "message": "Feedback created successfully"
}
Errors:
  • 400 Bad Request: Returned if the request payload contains invalid data types or invalid values for freeplay_feedback.
{
  "error": "Unexpected type for value. Values must be strings, numbers or booleans."
}
{
  "error": "Invalid value for 'freeplay_feedback': value. Must be 'positive' or 'negative'."
}

Trace Feedback

Trace feedback allows you to submit feedback for a specific trace in your project. Base URL: /api/v2/projects/<project-id>/trace-feedback

Create Trace Feedback

POST /id/<trace_id> Submits feedback for a trace, which can be used to track user satisfaction and improve model performance. Request Payload
ParameterTypeDescriptionRequired
feedback_attributestr / float / int / boolKey-value pairs of feedback attributesYes
freeplay_feedbackstrValue must be either "positive" or "negative"Yes
Example Request
curl 'https://app.freeplay.ai/api/v2/projects/<project-id>/trace-feedback/id/<trace-id>' \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer TOKEN' \
  --data '{
    "feedback_attribute_1": "value",
    "feedback_attribute_2": 10,
    "freeplay_feedback": "positive"
  }'
Sample Response Payload:
{
  "message": "Feedback created successfully"
}
Errors:
  • 400 Bad Request: Returned if the request payload contains invalid data types or invalid values for freeplay_feedback.
{
  "error": "Unexpected type for value. Values must be strings, numbers or booleans."
}
{
  "error": "Invalid value for 'freeplay_feedback': value. Must be 'positive' or 'negative'."
}

Datasets

Base URL: /api/v2/projects/<project-id>/datasets

Upload Dataset Test Cases

By ID

POST /id/<dataset-id>/test-cases Uploads test cases to an existing dataset.
Maximum of 100 test cases per request.
curl --location 'https://app.freeplay.ai/api/v2/projects/5688ebaf-7f22-4d5d-b9bb-bc715c8faabb/datasets/id/e7bc4931-2cd4-4345-88c1-9547fdb62f94/test-cases' \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer TOKEN' \
  --data '{
    "examples": [
      {
        "inputs": {
          "input_one": "value one a",
          "input_two": "value two a"
        },
        "output": "response one"
      },
      {
        "inputs": {
          "input_one": "value one a",
          "input_two": "value two b"
        },
        "output": "response two"
      }
    ]
  }'

Retrieve Dataset Metadata

By Name

GET /name/<dataset-name>
curl --location 'https://app.freeplay.ai/api/v2/projects/8f93dd00-2eb5-4ba2-9354-86d5c6831dfd/datasets/name/Sample' \
  --header 'Authorization: Bearer TOKEN'
Sample Response
{
  "description": "This is a test description",
  "id": "4995f471-7b3f-4719-b7cc-f211a84445eb",
  "name": "Sample"
}

By ID

GET /id/<dataset-id>
curl --location 'https://app.freeplay.ai/api/v2/projects/8f93dd00-2eb5-4ba2-9354-86d5c6831dfd/datasets/id/4995f471-7b3f-4719-b7cc-f211a84445eb' \
  --header 'Authorization: Bearer TOKEN'
Sample Response
{
  "description": "This is a test description",
  "id": "4995f471-7b3f-4719-b7cc-f211a84445eb",
  "name": "Sample"
}

Retrieve Dataset Test Cases

By Name

GET /name/<dataset-name>/test-cases
curl --location 'https://app.freeplay.ai/api/v2/projects/8f93dd00-2eb5-4ba2-9354-86d5c6831dfd/datasets/name/Sample/test-cases' \
  --header 'Authorization: Bearer TOKEN'
Sample Response
[
  {
    "history": null,
    "id": "115f950c-ca48-45c3-a372-1ef84cb9da44",
    "output": "Bullgle",
    "values": {
      "breedA": "Beagle",
      "breedB": "Bulldog"
    }
  },
  {
    "history": null,
    "id": "2cf1f967-8fc6-4ed7-a14e-97ffb1a3fce3",
    "output": "Porgi",
    "values": {
      "breedA": "Pug",
      "breedB": "Corgi"
    }
  },
  {
    "history": null,
    "id": "8ab1d78c-7e7d-4366-ac05-963780084aa0",
    "output": "Greatador",
    "values": {
      "breedA": "Labrador",
      "breedB": "Great Dane"
    }
  }
]

By ID

GET /id/<dataset-id>/test-cases
curl --location 'https://app.freeplay.ai/api/v2/projects/8f93dd00-2eb5-4ba2-9354-86d5c6831dfd/datasets/id/4995f471-7b3f-4719-b7cc-f211a84445eb/test-cases' \
  --header 'Authorization: Bearer TOKEN'
Sample Response
[
  {
    "history": null,
    "id": "115f950c-ca48-45c3-a372-1ef84cb9da44",
    "output": "Bullgle",
    "values": {
      "breedA": "Beagle",
      "breedB": "Bulldog"
    }
  },
  {
    "history": null,
    "id": "2cf1f967-8fc6-4ed7-a14e-97ffb1a3fce3",
    "output": "Porgi",
    "values": {
      "breedA": "Pug",
      "breedB": "Corgi"
    }
  },
  {
    "history": null,
    "id": "8ab1d78c-7e7d-4366-ac05-963780084aa0",
    "output": "Greatador",
    "values": {
      "breedA": "Labrador",
      "breedB": "Great Dane"
    }
  }
]

Completions

Base URL: /api/v2/projects/<project-id>/completions

Retrieve Completion Summary Statistics

Returns summary statistics for completions within a specified date range.
Statistics are served in a maximum date range of 30 days.
Request Payload
ParameterTypeDescriptionRequired
to_datestrLatest date for which sessions will be returned (exclusive). Format: YYYY-MM-DDNo (Default: Today’s date)
from_datestrEarliest date for which sessions will be returned (inclusive). Format: YYYY-MM-DDNo (Default: 7 days ago)
Validation Errors 400 Bad Request
{ "message": "Dates must be in YYYY-MM-DD format" }
{ "message": "To date must be greater than from date" }
{ "message": "Date range cannot exceed 30 days" }

Aggregate Statistics

POST /statistics Returns aggregated statistics across all prompt templates in the project.
curl --location 'https://app.freeplay.ai/api/v2/projects/45e20940-322a-4cb8-810f-3934a56aa5e2/completions/statistics' \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer TOKEN' \
  --data '{
    "to_date": "2025-01-15",
    "from_date": "2025-01-10"
  }'
Sample Response
{
  "summary_info": {
    "query-rewrite": {},
    "rag-directions": {},
    "rag-qa": {
      "2025-01-10": {
        "auto_evals": {
          "Answer Accuracy": {
            "4": 4,
            "5": 12
          },
          "Answer Faithfulness": {
            "no": 1,
            "yes": 1
          },
          "Context Relevance": {
            "4": 5,
            "5": 10
          }
        },
        "client_evals": {},
        "human_evals": {}
      },
      "2025-01-11": {
        "auto_evals": {
          "Answer Accuracy": {
            "4": 2,
            "5": 12
          },
          "Answer Faithfulness": {
            "yes": 2
          },
          "Context Relevance": {
            "4": 4,
            "5": 12
          }
        },
        "client_evals": {},
        "human_evals": {}
      },
      "2025-01-12": {
        "auto_evals": {
          "Answer Accuracy": {
            "4": 2,
            "5": 14
          },
          "Answer Faithfulness": {
            "yes": 2
          },
          "Context Relevance": {
            "4": 2,
            "5": 10
          }
        },
        "client_evals": {},
        "human_evals": {}
      },
      "2025-01-13": {
        "auto_evals": {
          "Answer Accuracy": {
            "4": 3,
            "5": 10
          },
          "Context Relevance": {
            "4": 5,
            "5": 6
          }
        },
        "client_evals": {},
        "human_evals": {}
      },
      "2025-01-14": {
        "auto_evals": {
          "Answer Accuracy": {
            "5": 13
          },
          "Context Relevance": {
            "4": 1,
            "5": 7
          }
        },
        "client_evals": {},
        "human_evals": {}
      }
    },
    "rag-qa-common-elements": {},
    "rag-qa-structured": {},
    "rag-query": {}
  }
}

By Prompt Template ID

POST /statistics/<prompt-template-id> Returns statistics for completions associated with a specific prompt template.
curl --location 'https://app.freeplay.ai/api/v2/projects/45e20940-322a-4cb8-810f-3934a56aa5e2/completions/statistics/0053c449-c736-4557-a3d4-bf3b852d0604' \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer TOKEN' \
  --data '{
    "to_date": "2025-01-15",
    "from_date": "2025-01-10"
  }'
Sample Response
{
  "summary_info": {
    "rag-qa": {
      "2025-01-10": {
        "auto_evals": {
          "Answer Accuracy": {
            "4": 4,
            "5": 12
          },
          "Answer Faithfulness": {
            "no": 1,
            "yes": 1
          },
          "Context Relevance": {
            "4": 5,
            "5": 10
          }
        },
        "client_evals": {},
        "human_evals": {}
      },
      "2025-01-11": {
        "auto_evals": {
          "Answer Accuracy": {
            "4": 2,
            "5": 12
          },
          "Answer Faithfulness": {
            "yes": 2
          },
          "Context Relevance": {
            "4": 4,
            "5": 12
          }
        },
        "client_evals": {},
        "human_evals": {}
      },
      "2025-01-12": {
        "auto_evals": {
          "Answer Accuracy": {
            "4": 2,
            "5": 14
          },
          "Answer Faithfulness": {
            "yes": 2
          },
          "Context Relevance": {
            "4": 2,
            "5": 10
          }
        },
        "client_evals": {},
        "human_evals": {}
      },
      "2025-01-13": {
        "auto_evals": {
          "Answer Accuracy": {
            "4": 3,
            "5": 10
          },
          "Context Relevance": {
            "4": 5,
            "5": 6
          }
        },
        "client_evals": {},
        "human_evals": {}
      },
      "2025-01-14": {
        "auto_evals": {
          "Answer Accuracy": {
            "5": 13
          },
          "Context Relevance": {
            "4": 1,
            "5": 7
          }
        },
        "client_evals": {},
        "human_evals": {}
      }
    }
  }
}