{
  "openapi": "3.1.0",
  "info": {
    "title": "Fidget Labs API",
    "version": "1.0.0",
    "summary": "EU AI Act risk classification, free and unauthenticated.",
    "description": "Fidget Labs is a composable and AI architecture consultancy. This API exposes the\nclassification engine behind our free EU AI Act Risk Check as a callable endpoint,\nso an agent can answer a regulatory scoping question without a person filling in a form.\n\nNo authentication. No API key. Nothing is stored: no submission is written to a\ndatabase, emailed, or attached to a CRM record. There is no published rate limit; both\noperations are pure computation over the request body. Contact us before sustained volume.\n\nResults are informational and not legal advice. Every response carries a `disclaimer`\nfield saying so, and it should be relayed alongside any answer built from this data.",
    "contact": {
      "name": "Fidget Labs",
      "email": "hello@fidgetlabs.io",
      "url": "https://fidgetlabs.io/developers"
    },
    "license": {
      "name": "Free to use, attribution appreciated",
      "url": "https://fidgetlabs.io/terms"
    }
  },
  "servers": [
    {
      "url": "https://fidgetlabs.io",
      "description": "Production"
    }
  ],
  "externalDocs": {
    "description": "Developer documentation",
    "url": "https://fidgetlabs.io/developers"
  },
  "tags": [
    {
      "name": "EU AI Act",
      "description": "Classify an AI use against the risk tiers of Regulation (EU) 2024/1689."
    }
  ],
  "paths": {
    "/api/v1/eu-ai-act/classify": {
      "post": {
        "operationId": "classifyEuAiActRisk",
        "tags": [
          "EU AI Act"
        ],
        "summary": "Classify an AI use against the EU AI Act risk tiers",
        "description": "Takes a description of how an organisation builds or uses AI and returns which\nEU AI Act risk tiers it triggers, which obligations attach, and the compliance\ndeadline for each tier.\n\nCall GET /api/v1/eu-ai-act/questions first if you need the wording of each question\nor the permitted values. Every field is required except `industryOther`. The two\narray fields accept an empty array, which means none of the listed options apply.\n\nStateless and anonymous. Nothing about the request is retained.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Answers"
              },
              "examples": {
                "hrScreening": {
                  "summary": "A mid-size firm screening job applicants with AI",
                  "value": {
                    "size": "100_1000",
                    "industry": "hr_staffing",
                    "role": "director",
                    "transparencyInteraction": "yes",
                    "transparencyContent": "no",
                    "roleSignal": "builder",
                    "aiFrequency": "daily",
                    "annexDomains": [
                      "employment"
                    ],
                    "biometricUse": "no",
                    "prohibited": []
                  }
                },
                "internalAssistant": {
                  "summary": "A small firm using an off-the-shelf assistant internally",
                  "value": {
                    "size": "1_50",
                    "industry": "professional_services",
                    "role": "csuite",
                    "transparencyInteraction": "no",
                    "transparencyContent": "no",
                    "roleSignal": "user",
                    "aiFrequency": "weekly",
                    "annexDomains": [],
                    "biometricUse": "no",
                    "prohibited": []
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The classification.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Classification"
                }
              }
            }
          },
          "400": {
            "description": "The body was not valid JSON.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "Wrong method. The Allow header lists what is accepted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "The body parsed but failed validation. `fieldErrors` names the fields.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Back off and retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "A fault on our side. The request was fine.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/eu-ai-act/questions": {
      "get": {
        "operationId": "getEuAiActQuestions",
        "tags": [
          "EU AI Act"
        ],
        "summary": "Get the questionnaire behind the classifier",
        "description": "Returns every field the classify endpoint accepts, the question each one answers in\nthe wording a person sees, and the permitted values with their labels.\n\nUse this to construct a valid classify request, or to ask a person the questions in\ntheir own words before calling it. Takes no parameters.",
        "responses": {
          "200": {
            "description": "The questionnaire.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Questionnaire"
                }
              }
            }
          },
          "405": {
            "description": "Wrong method.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Answers": {
        "type": "object",
        "properties": {
          "size": {
            "type": "string",
            "enum": [
              "1_50",
              "50_100",
              "100_1000",
              "1000_5000",
              "5000_plus"
            ],
            "description": "How large is your organization?"
          },
          "industry": {
            "type": "string",
            "enum": [
              "financial_services",
              "healthcare",
              "technology",
              "retail",
              "manufacturing",
              "professional_services",
              "education",
              "government",
              "media",
              "hr_staffing",
              "other"
            ],
            "description": "What industry are you in?"
          },
          "industryOther": {
            "type": "string",
            "maxLength": 120,
            "description": "Free text naming the industry. Only meaningful when `industry` is \"other\"."
          },
          "role": {
            "type": "string",
            "enum": [
              "specialist",
              "director",
              "vp",
              "csuite"
            ],
            "description": "What is your role?"
          },
          "transparencyInteraction": {
            "type": "string",
            "enum": [
              "yes",
              "no",
              "unsure"
            ],
            "description": "Does your business use AI that talks to your customers or the public? For example, a chatbot on your website, an AI assistant in your app, or an automated system that answers customer questions by phone or message."
          },
          "transparencyContent": {
            "type": "string",
            "enum": [
              "yes",
              "no",
              "unsure"
            ],
            "description": "Does your business use AI to create or edit content that you publish or send out? For example, marketing copy, social posts, product descriptions, images, audio, or video where AI created or significantly changed what you publish or send."
          },
          "roleSignal": {
            "type": "string",
            "enum": [
              "user",
              "builder",
              "both",
              "unsure"
            ],
            "description": "Which best describes how your business gets the AI it uses? This is about where your AI comes from, not how it works. 'Building or heavily customizing' means your team trains, fine-tunes, or substantially modifies AI models. 'Using tools others built' means you use AI products or features made by other companies, roughly as they come."
          },
          "aiFrequency": {
            "type": "string",
            "enum": [
              "occasional",
              "monthly",
              "weekly",
              "daily",
              "embedded"
            ],
            "description": "How often does your business use AI? Including internal tools. Count anything where AI helps with analysis, drafting, scheduling, coding, research, or any task, even if customers never see it."
          },
          "annexDomains": {
            "maxItems": 4,
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "employment",
                "credit_insurance",
                "essential_services",
                "education"
              ]
            },
            "description": "Does AI help make or influence any decisions about people in your business? Select any that apply. This is about decisions that meaningfully affect a person, where AI plays a role in the outcome."
          },
          "biometricUse": {
            "type": "string",
            "enum": [
              "yes",
              "no",
              "unsure"
            ],
            "description": "Does your business use AI that recognizes or analyzes people's physical characteristics? For example, systems that identify people by face or voice, or that analyze physical or biometric characteristics."
          },
          "prohibited": {
            "maxItems": 5,
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "manipulation",
                "social_scoring",
                "rbi",
                "emotion",
                "predictive_policing"
              ]
            },
            "description": "Could any of your AI uses fall into these categories? These are uses the EU has banned outright. Most businesses will select none. Select any you are unsure about. Selecting one is not a verdict. It flags something worth an urgent closer look."
          }
        },
        "required": [
          "size",
          "industry",
          "role",
          "transparencyInteraction",
          "transparencyContent",
          "roleSignal",
          "aiFrequency",
          "annexDomains",
          "biometricUse",
          "prohibited"
        ],
        "additionalProperties": false
      },
      "Classification": {
        "type": "object",
        "required": [
          "riskLevel",
          "riskLevelName",
          "summary",
          "levels",
          "role",
          "urgency",
          "smeTier",
          "disclaimer"
        ],
        "properties": {
          "riskLevel": {
            "type": "string",
            "enum": [
              "prohibited",
              "high",
              "transparency",
              "limited"
            ],
            "description": "The highest tier the answers trigger. Always present: `limited` is the floor."
          },
          "riskLevelName": {
            "type": "string"
          },
          "summary": {
            "type": "string",
            "description": "One paragraph a person can read as-is."
          },
          "levels": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "level",
                "name",
                "hit",
                "uncertain",
                "deadline",
                "meaning",
                "maximumPenalty"
              ],
              "properties": {
                "level": {
                  "type": "string",
                  "enum": [
                    "prohibited",
                    "high",
                    "transparency",
                    "limited"
                  ],
                  "description": "The risk tier this row describes."
                },
                "name": {
                  "type": "string",
                  "description": "The tier's name in the Act."
                },
                "hit": {
                  "type": "boolean",
                  "description": "Whether the submitted answers trigger this tier."
                },
                "uncertain": {
                  "type": "boolean",
                  "description": "True when the tier was triggered only by \"not sure\" answers. Treat it as worth confirming rather than as a finding."
                },
                "deadline": {
                  "type": "string",
                  "description": "When this tier applies, in words."
                },
                "deadlineIso": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "The same date as ISO 8601, or null where the tier is an ongoing baseline."
                },
                "meaning": {
                  "type": "string",
                  "description": "What the tier covers, in plain language."
                },
                "maximumPenalty": {
                  "type": "string",
                  "description": "The ceiling on penalties for this tier."
                }
              }
            },
            "description": "Every tier, in severity order, whether triggered or not."
          },
          "role": {
            "type": "object",
            "required": [
              "lean",
              "explanation"
            ],
            "properties": {
              "lean": {
                "type": "string",
                "enum": [
                  "provider",
                  "deployer",
                  "unclear"
                ],
                "description": "Which set of obligations attaches. Providers carry the heavier half."
              },
              "explanation": {
                "type": "string"
              }
            }
          },
          "urgency": {
            "type": "string",
            "enum": [
              "elevated",
              "steady"
            ],
            "description": "Elevated when AI is used daily or is embedded in the product."
          },
          "smeTier": {
            "type": "boolean",
            "description": "True for organisations small enough that the lower fine figure applies."
          },
          "triggers": {
            "type": "object",
            "description": "The specific answers that drove the classification.",
            "properties": {
              "annexDomains": {
                "type": "array",
                "items": {
                  "type": "object",
                  "required": [
                    "value",
                    "label",
                    "note"
                  ],
                  "properties": {
                    "value": {
                      "type": "string"
                    },
                    "label": {
                      "type": "string"
                    },
                    "note": {
                      "type": "string",
                      "description": "Why this selection matters under the Act."
                    }
                  }
                }
              },
              "prohibited": {
                "type": "array",
                "items": {
                  "type": "object",
                  "required": [
                    "value",
                    "label",
                    "note"
                  ],
                  "properties": {
                    "value": {
                      "type": "string"
                    },
                    "label": {
                      "type": "string"
                    },
                    "note": {
                      "type": "string",
                      "description": "Why this selection matters under the Act."
                    }
                  }
                }
              },
              "biometricUse": {
                "type": "string",
                "enum": [
                  "yes",
                  "no",
                  "unsure"
                ]
              },
              "transparencyInteraction": {
                "type": "string",
                "enum": [
                  "yes",
                  "no",
                  "unsure"
                ]
              },
              "transparencyContent": {
                "type": "string",
                "enum": [
                  "yes",
                  "no",
                  "unsure"
                ]
              }
            }
          },
          "profile": {
            "type": "object",
            "description": "The submitted organisation profile, echoed back with labels.",
            "properties": {
              "size": {
                "type": "object",
                "required": [
                  "value",
                  "label"
                ],
                "properties": {
                  "value": {
                    "type": "string",
                    "description": "The enum value as submitted."
                  },
                  "label": {
                    "type": "string",
                    "description": "The same value in words."
                  }
                }
              },
              "industry": {
                "type": "object",
                "required": [
                  "value",
                  "label"
                ],
                "properties": {
                  "value": {
                    "type": "string",
                    "description": "The enum value as submitted."
                  },
                  "label": {
                    "type": "string",
                    "description": "The same value in words."
                  }
                }
              },
              "role": {
                "type": "object",
                "required": [
                  "value",
                  "label"
                ],
                "properties": {
                  "value": {
                    "type": "string",
                    "description": "The enum value as submitted."
                  },
                  "label": {
                    "type": "string",
                    "description": "The same value in words."
                  }
                }
              },
              "aiFrequency": {
                "type": "object",
                "required": [
                  "value",
                  "label"
                ],
                "properties": {
                  "value": {
                    "type": "string",
                    "description": "The enum value as submitted."
                  },
                  "label": {
                    "type": "string",
                    "description": "The same value in words."
                  }
                }
              }
            }
          },
          "fullReport": {
            "type": "string",
            "format": "uri",
            "description": "Where a person can get the long-form report, with sources."
          },
          "disclaimer": {
            "type": "string",
            "description": "Informational, not legal advice. Relay this alongside any result."
          }
        }
      },
      "Questionnaire": {
        "type": "object",
        "required": [
          "questionCount",
          "questions",
          "disclaimer"
        ],
        "properties": {
          "questionCount": {
            "type": "integer",
            "description": "How many questions there are."
          },
          "note": {
            "type": "string",
            "description": "How to turn these answers into a request."
          },
          "questions": {
            "type": "array",
            "description": "In presentation order, which is not significant to scoring.",
            "items": {
              "type": "object",
              "required": [
                "field",
                "kind",
                "text",
                "options"
              ],
              "properties": {
                "field": {
                  "type": "string",
                  "description": "The property name to set on the classify request body."
                },
                "kind": {
                  "type": "string",
                  "enum": [
                    "single",
                    "multi"
                  ],
                  "description": "`multi` fields take an array; `single` fields take one value."
                },
                "text": {
                  "type": "string",
                  "description": "The question, as a person is asked it."
                },
                "helper": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "Clarifying detail, if any."
                },
                "options": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "required": [
                      "value",
                      "label",
                      "clearsOthers"
                    ],
                    "properties": {
                      "value": {
                        "type": "string",
                        "description": "Send this in the request body."
                      },
                      "label": {
                        "type": "string",
                        "description": "Show this to a person."
                      },
                      "clearsOthers": {
                        "type": "boolean",
                        "description": "True for a \"none of these\" option, which is mutually exclusive with the rest."
                      }
                    }
                  }
                },
                "otherField": {
                  "type": [
                    "object",
                    "null"
                  ],
                  "description": "A free-text follow-up, where the question has one.",
                  "properties": {
                    "field": {
                      "type": "string"
                    },
                    "label": {
                      "type": "string"
                    },
                    "maxLength": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "disclaimer": {
            "type": "string"
          }
        }
      },
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message",
              "hint"
            ],
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "invalid_json",
                  "invalid_answers",
                  "method_not_allowed",
                  "not_found",
                  "rate_limited",
                  "internal_error"
                ],
                "description": "Stable machine-readable code. Branch on this, not on `message`."
              },
              "message": {
                "type": "string",
                "description": "What went wrong, for a person."
              },
              "hint": {
                "type": "string",
                "description": "What to do about it."
              },
              "fieldErrors": {
                "type": "object",
                "additionalProperties": {
                  "type": "string"
                },
                "description": "Present on validation failures only. Keyed by dotted field path, one message per field."
              }
            }
          }
        }
      }
    }
  }
}