Asenion AI GraphQL API Guide
Overview
This guide provides comprehensive documentation for accessing the Asenion.ai GraphQL API for server-to-server integrations, including the Archer IRM connector.
Base URL: https://fairly.ai/graphql
Authentication: Bearer Token (no CSRF required for backend clients)
Table of Contents
- Authentication
- Common Queries
- Common Mutations
- Data Types Reference
- Error Handling
- Rate Limiting
- Best Practices
Authentication
Generate Bearer Token
Before making GraphQL requests, you need to generate a Bearer token.
Option 1: Using GraphQL (One-time setup)
First, authenticate with username/password to get session cookies:
curl -X POST "https://fairly.ai/auth/login" \
-H "Content-Type: application/json" \
-d '{
"username": "your-email@company.com",
"password": "your-password"
}' \
-c cookies.txt \
-b cookies.txt
Then generate a Bearer token:
curl -X POST "https://fairly.ai/graphql" \
-H "Content-Type: application/json" \
-H "X-CSRF-Token: YOUR_CSRF_TOKEN_FROM_COOKIES" \
-b cookies.txt \
-d '{
"query": "mutation GenerateBearerToken($input: NewTokenGenerationInput!) { generateBearerAccessToken(input: $input) { token { token description expirationDate createdAt } } }",
"variables": {
"input": {
"email": "your-email@company.com",
"description": "Archer IRM Integration Token"
}
}
}'
Response:
{
"data": {
"generateBearerAccessToken": {
"token": {
"token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"description": "Archer IRM Integration Token",
"expirationDate": "2026-03-09T00:00:00.000Z",
"createdAt": "2025-12-09T10:30:00.000Z"
}
}
}
}
Option 2: Using the UI
- Log in to Fairly.ai
- Navigate to Settings → API Access (or Settings → Connections)
- Click “Generate API Token”
- Enter a description (e.g., “Archer IRM Integration”)
- Copy the token (it will only be shown once)
Using the Bearer Token
Include the token in the Authorization header of all GraphQL requests:
curl -X POST "https://fairly.ai/graphql" \
-H "Authorization: Bearer YOUR_TOKEN_HERE" \
-H "Content-Type: application/json" \
-d '{
"query": "{ projectList { _id name } }"
}'
Common Queries
1. List All Projects (AI Systems)
Retrieve all AI systems/projects accessible to the authenticated user.
Query:
query GetAllProjects {
projectList {
_id
name
description
projectType
orgId
organizationUnitId
source
riskStatus
riskScore
riskDimensions {
identifier
status
score
}
completionPercent
numControls
numAnswers
numTestScores
createdAt
updatedAt
createdBy
lifecycle {
_id
name
currentStage {
_id
name
}
}
assessments {
_id
name
description
status
riskStatus
riskScore
completionPercent
numControls
numAnswers
updatedAt
}
policyList {
identifier
version
policyId
}
}
}
cURL Example:
curl -X POST "https://fairly.ai/graphql" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"query": "query GetAllProjects { projectList { _id name projectType riskStatus riskScore completionPercent assessments { _id name status riskStatus completionPercent } } }"}'
Response:
{
"data": {
"projectList": [
{
"_id": "64abc123...",
"name": "Customer Service Chatbot",
"projectType": "AI_SYSTEM",
"riskStatus": "MEDIUM",
"riskScore": 65.5,
"completionPercent": 78.2,
"assessments": [
{
"_id": "assessment_123",
"name": "EU AI Act Compliance",
"status": "IN_PROGRESS",
"riskStatus": "MEDIUM",
"completionPercent": 78.2
}
]
}
]
}
}
2. Get Specific Project Details
Retrieve detailed information about a specific project.
Query:
query GetProject($projectId: ID!) {
projectFromId(_id: $projectId) {
_id
name
description
projectType
source
riskStatus
riskScore
inherentRiskStatus
riskDimensions {
identifier
status
score
}
alignmentStatus
alignmentRiskData {
status
aligned
notAligned
notAvailable
}
technologyRiskData {
status
numLowRisk
numMediumRisk
numHighRisk
}
completionPercent
numControls
numAnswers
numTestScores
createdAt
updatedAt
createdBy
lifecycle {
_id
name
currentStage {
_id
name
description
}
}
assessments {
_id
name
description
status
riskStatus
riskScore
maxRiskScore
unacceptableRiskPercent
acceptableRiskPercent
completionPercent
numControls
numAnswers
updatedAt
enableVersioning
latestVersionId
policy {
_id
identifier
version
name
description
jurisdiction
}
answerBundles {
_id
controlBundleId
numControls
numAnswers
completionPercent
alignmentStatus
maxScore
rawScore
updatedAt
}
assignments {
assignedTo
assignedBy
assignedToName
dueDate
type
status
comments
completedAt
}
}
policyList {
identifier
version
policyId
}
sharedWith {
_id
email
role
firstName
lastName
}
}
}
cURL Example:
curl -X POST "https://fairly.ai/graphql" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"query": "query GetProject($projectId: ID!) { projectFromId(_id: $projectId) { _id name riskStatus riskScore riskDimensions { identifier status score } assessments { _id name status riskStatus completionPercent } } }",
"variables": {
"projectId": "64abc123..."
}
}'
3. Get Project Statistics (Paginated)
Retrieve paginated list of AI systems with filtering options.
Query:
query GetAISystems($pagination: PaginationInput, $organizationUnitId: ID) {
aiSystemList(
pagination: $pagination
organizationUnitId: $organizationUnitId
) {
projects {
_id
name
description
projectType
riskStatus
riskScore
alignmentStatus
completionPercent
numControls
numAnswers
createdAt
createdBy
securityStatus
privacyStatus
biasStatus
}
totalCount
hasNextPage
hasPreviousPage
}
}
Variables:
{
"pagination": {
"page": 1,
"pageSize": 20,
"alignmentFilter": "ALL",
"riskFilter": "ALL",
"sortDirection": "DESC"
},
"organizationUnitId": null
}
4. Get Dashboard Metrics
Retrieve high-level dashboard metrics for AI systems.
Query:
query GetDashboardMetrics($organizationUnitId: ID) {
aiSystemDashboardMetrics(organizationUnitId: $organizationUnitId) {
totalAISystems
totalRiskScore
totalControls
totalTests
totalAIUseCases
}
aiSystemRiskDistribution(organizationUnitId: $organizationUnitId) {
low
medium
high
}
aiSystemAlignmentDistribution(organizationUnitId: $organizationUnitId) {
aligned
notAligned
}
}
5. Get Assessment Details
Retrieve detailed information about a specific assessment.
Query:
query GetAssessmentWithVersion($assessmentId: ID!, $versionId: ID!) {
assessmentWithVersion(assessmentId: $assessmentId, versionId: $versionId) {
_id
name
description
status
riskStatus
riskScore
maxRiskScore
completionPercent
numControls
numAnswers
updatedAt
policy {
_id
identifier
version
name
description
jurisdiction
}
answerBundles {
_id
controlBundleId
numControls
numAnswers
completionPercent
alignmentStatus
answers {
_id
controlBundleId
controlIdentifier
alignmentStatus
riskStatus
riskScore
values {
answerOptionId
value
valueType
extraValue
labels
createdAt
updatedAt
}
createdAt
updatedAt
}
}
}
}
6. Get User Tasks (Paginated)
Retrieve tasks assigned to the authenticated user.
Query:
query GetUserTasks($input: UserTasksInput!) {
userTasksPaginated(input: $input) {
tasks {
taskId
projectId
projectName
projectType
projectTypeLabel
assessmentId
assessmentName
policyName
assignmentType
status
dueDate
assignedBy
assignedTo
assignedToName
comments
completedAt
}
totalCount
hasNextPage
hasPreviousPage
}
}
Variables:
{
"input": {
"pagination": {
"page": 1,
"pageSize": 20,
"sortDirection": "DESC"
},
"filter": "ASSIGNED",
"status": "PENDING"
}
}
Common Mutations
1. Create AI System
Create a new AI system/project.
Mutation:
mutation CreateAISystem($input: CreateAISystemInput!) {
createAISystem(input: $input) {
_id
name
description
projectType
source
orgId
organizationUnitId
createdAt
createdBy
policyList {
identifier
version
policyId
}
}
}
Variables:
{
"input": {
"name": "Customer Support AI",
"description": "AI-powered customer support system",
"projectType": "AI_SYSTEM",
"source": "Fairly",
"organizationUnitId": "orgunit_123",
"policies": [
{
"policy": {
"identifier": "eu_ai_act",
"version": "1.0"
},
"bundles": [
{
"controlBundleId": "bundle_123"
}
]
}
],
"members": [
{
"email": "user@company.com",
"role": "ADMIN"
}
]
}
}
2. Add Assessment to Project
Add a new assessment to an existing project.
Mutation:
mutation AddAssessment($input: AddAssessmentInput!) {
addProjectAssessment(input: $input) {
project {
_id
name
assessments {
_id
name
description
status
riskStatus
completionPercent
}
}
}
}
Variables:
{
"input": {
"projectId": "64abc123...",
"name": "EU AI Act Compliance Assessment",
"description": "Assessment against EU AI Act requirements",
"projectPolicy": {
"policy": {
"identifier": "eu_ai_act",
"version": "1.0"
}
}
}
}
3. Update Assessment Answer
Submit an answer to an assessment control.
Mutation:
mutation UpdateAnswer($input: UpdateAnswerInput!) {
assessmentUpdateAnswer(input: $input) {
assessment {
_id
completionPercent
status
updatedAt
}
updatedBundle {
_id
controlBundleId
completionPercent
numAnswers
}
}
}
Variables:
{
"input": {
"assessmentId": "assessment_123",
"controlBundleId": "bundle_456",
"controlIdentifier": "control_789",
"values": [
{
"answerOptionId": "option_1",
"value": "Yes",
"labels": ["compliance"],
"comment": ["Implemented per requirements"]
}
]
}
}
4. Update Assessment Status
Change the status of an assessment (e.g., submit for review, approve).
Mutation:
mutation UpdateAssessmentStatus($input: UpdateAssessmentStatusInput!) {
assessmentUpdateStatus(input: $input) {
assessment {
_id
name
status
updatedAt
}
}
}
Variables:
{
"input": {
"assessmentId": "assessment_123",
"status": "SUBMITTED",
"comments": "Ready for review",
"sendNotification": true,
"notificationData": {
"to": "reviewer@company.com",
"from": "user@company.com",
"assignedMember": "Reviewer Name",
"projectName": "Customer Support AI",
"projectId": "64abc123...",
"assessmentName": "EU AI Act Compliance",
"purpose": "Please review this assessment"
}
}
}
5. Assign Assessment for Review
Assign an assessment to users for review or approval.
Mutation:
mutation AssignAssessment($input: AssessmentAssignReviewInput!) {
assignAssessmentAction(input: $input) {
assessment {
_id
name
assignments {
assignedTo
assignedBy
assignedToName
dueDate
type
status
}
}
}
}
Variables:
{
"input": {
"assessmentId": "assessment_123",
"assignedTo": [
{
"email": "reviewer@company.com",
"name": "Reviewer Name"
}
],
"dueDate": "2025-12-31T23:59:59.000Z",
"type": "REVIEW",
"sendNotification": true,
"notificationData": {
"to": ["reviewer@company.com"],
"from": "user@company.com",
"assigner": "User Name",
"role": "Reviewer",
"projectName": "Customer Support AI",
"projectId": "64abc123...",
"assessmentName": "EU AI Act Compliance",
"purpose": "Please review this assessment"
}
}
}
6. Add MLflow Results
Add MLflow tracking results to a project assessment.
Mutation:
mutation AddMLflowInfo($input: AddMLflowInfoInput!) {
addMLflowInfo(input: $input) {
project {
_id
name
assessments {
_id
mlflowResults {
mlflowUrl
experimentId
runId
modelName
}
}
}
error
}
}
Variables:
{
"input": {
"projectId": "64abc123...",
"assessmentId": "assessment_123",
"mlflowInfo": {
"mlflowUrl": "https://mlflow.yourcompany.com",
"experimentId": "exp_456",
"runId": "run_789",
"modelName": "customer_service_v2",
"additionalMetadata": {
"framework": "tensorflow",
"version": "2.1.0"
}
}
}
}
Data Types Reference
Project Type
type Project {
_id: ID!
name: NEString!
description: String
projectType: ProjectType! # AI_SYSTEM, MODEL_CANDIDATE, DATASET, etc.
source: Source # Fairly, Azure, GCP, AWS
orgId: NEString!
organizationUnitId: ID
riskStatus: AssessmentRiskStatusType # LOW, MEDIUM, HIGH, UNACCEPTABLE
riskScore: Float
riskDimensions: [RiskDimension!]
alignmentStatus: AlignmentStatus
completionPercent: Float
numControls: Int
numAnswers: Int
assessments: [Assessment!]!
policyList: [PolicyRef!]!
lifecycle: LifeCycle
createdAt: Timestamp
updatedAt: Timestamp
createdBy: NEString
}
Assessment Type
type Assessment {
_id: ID!
name: NEString!
description: NEString
policy: Policy!
status: AssessmentStatusType! # NOT_STARTED, IN_PROGRESS, SUBMITTED, IN_REVIEW, APPROVED, REJECTED
riskStatus: AssessmentRiskStatusType
riskScore: Float
maxRiskScore: Float
unacceptableRiskPercent: Float
acceptableRiskPercent: Float
completionPercent: Float
numControls: Float
numAnswers: Float
answerBundles: [AnswerBundle!]!
assignments: [AssignmentRef]
updatedAt: Timestamp
}
Risk Dimension Type
type RiskDimension {
identifier: String! # e.g., "operational_risk", "model_risk", "compliance_risk"
status: AssessmentRiskStatusType!
score: Float!
}
Enums
enum ProjectType {
AI_SYSTEM
MODEL_CANDIDATE
MODEL_CHAMPION
FUNCTIONAL_MODEL
DATASET
VENDOR_AGENT
AGENT_CANDIDATE
VENDOR_MODEL
ORGANIZATION
}
enum AssessmentStatusType {
NOT_STARTED
IN_PROGRESS
SUBMITTED
IN_REVIEW
APPROVED
REJECTED
}
enum AssessmentRiskStatusType {
LOW
MEDIUM
HIGH
UNACCEPTABLE
}
enum AlignmentStatus {
ALIGNED
NOT_ALIGNED
N_A
}
enum Source {
Fairly
Azure
GCP
AWS
}
Error Handling
Authentication Errors
{
"errors": [
{
"message": "Invalid token",
"extensions": {
"code": "UNAUTHENTICATED"
}
}
]
}
Solutions:
- Verify Bearer token is correct
- Check if token has expired
- Generate a new token if needed
Authorization Errors
{
"errors": [
{
"message": "Access denied. You do not have permission to access this project.",
"extensions": {
"code": "FORBIDDEN"
}
}
]
}
Solutions:
- Ensure the user account has appropriate permissions
- Check if the project is shared with the user
- Verify organization membership
Validation Errors
{
"errors": [
{
"message": "Variable \"$projectId\" of required type \"ID!\" was not provided.",
"extensions": {
"code": "BAD_USER_INPUT"
}
}
]
}
Solutions:
- Check that all required variables are provided
- Validate variable types match the schema
- Review query syntax
Rate Limiting
Current Limits:
- 1000 requests per hour per token
- 100 requests per minute per token
- Burst limit: 20 requests per 10 seconds
Rate Limit Headers:
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 995
X-RateLimit-Reset: 1640995200
Rate Limit Exceeded Response:
{
"errors": [
{
"message": "Rate limit exceeded. Please try again in 60 seconds.",
"extensions": {
"code": "RATE_LIMIT_EXCEEDED",
"retryAfter": 60
}
}
]
}
Best Practices
1. Use Field Selection
Only request fields you need to reduce response size and improve performance:
# ❌ Bad: Requesting all fields
query GetProjects {
projectList {
_id
name
description
# ... 30+ more fields
}
}
# ✅ Good: Only necessary fields
query GetProjects {
projectList {
_id
name
riskStatus
riskScore
}
}
2. Use Pagination
For large datasets, always use pagination:
query GetAISystems($pagination: PaginationInput!) {
aiSystemList(pagination: $pagination) {
projects {
_id
name
riskScore
}
totalCount
hasNextPage
}
}
3. Handle Errors Gracefully
const response = await fetch("https://fairly.ai/graphql", {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ query, variables }),
});
const result = await response.json();
if (result.errors) {
console.error("GraphQL Errors:", result.errors);
// Handle specific error codes
for (const error of result.errors) {
if (error.extensions?.code === "UNAUTHENTICATED") {
// Refresh token or re-authenticate
}
}
} else {
// Process data
console.log("Data:", result.data);
}
4. Use Variables for Dynamic Queries
# ✅ Good: Use variables
query GetProject($projectId: ID!) {
projectFromId(_id: $projectId) {
_id
name
}
}
# ❌ Bad: Hard-coded values
query GetProject {
projectFromId(_id: "64abc123...") {
_id
name
}
}
5. Batch Related Queries
# ✅ Good: Single request for related data
query GetProjectAndMetrics($projectId: ID!) {
projectFromId(_id: $projectId) {
_id
name
riskScore
}
aiSystemDashboardMetrics {
totalAISystems
totalRiskScore
}
}
# ❌ Bad: Multiple separate requests
# query1: projectFromId
# query2: aiSystemDashboardMetrics
6. Implement Token Refresh
class FairlyAPIClient {
constructor(token, tokenExpiration) {
this.token = token;
this.tokenExpiration = new Date(tokenExpiration);
}
async query(query, variables) {
// Check if token is expiring soon (within 7 days)
const sevenDaysFromNow = new Date();
sevenDaysFromNow.setDate(sevenDaysFromNow.getDate() + 7);
if (this.tokenExpiration < sevenDaysFromNow) {
await this.refreshToken();
}
return this.executeQuery(query, variables);
}
async refreshToken() {
// Generate new token via GraphQL
// Update this.token and this.tokenExpiration
}
}
7. Use Persistent HTTP Connections
// Node.js example
const https = require("https");
const agent = new https.Agent({
keepAlive: true,
maxSockets: 10,
});
fetch("https://fairly.ai/graphql", {
agent,
method: "POST",
// ... rest of config
});