{
 "openapi": "3.0.3",
 "info": {
  "title": "Tuatara Decide",
  "version": "1.0",
  "description": "Train a specialist text classifier from your labelled examples (2 to 255 labels, at least 2 examples per label) in about a minute, then label texts with it in well under a second: each result carries the label, its confidence, every label's probability and unsure=true under your threshold. Built on Cymetica's own Tuatara vector model with a TF-IDF blend and an embedding head; on the public BANKING77 benchmark the embedding-head configuration scored 94.09% where the leading hosted classifier scored 92.40% in the same harness (scorer and every prediction published: https://cymetica.com/blog/matching-jev-on-banking77-at-a-thousandth-of-the-cost). Your examples train only your classifier and are never shared. Unsure texts can optionally go to your own general-model key (X-Fallback-Key), which is never stored.",
  "contact": {
   "name": "Cymetica",
   "url": "https://cymetica.com",
   "email": "contact@cymetica.com"
  },
  "termsOfService": "https://cymetica.com/terms",
  "license": {
   "name": "Proprietary"
  }
 },
 "servers": [
  {
   "url": "https://cymetica.com/api/v1/decide",
   "description": "Production (Japan)"
  }
 ],
 "security": [
  {
   "apiKey": []
  },
  {
   "bearer": []
  }
 ],
 "tags": [
  {
   "name": "Classifiers",
   "description": "Train, inspect, update and delete classifiers"
  },
  {
   "name": "Decide",
   "description": "Label texts"
  },
  {
   "name": "Info",
   "description": "What the service is"
  }
 ],
 "components": {
  "securitySchemes": {
   "apiKey": {
    "type": "apiKey",
    "in": "header",
    "name": "X-API-Key",
    "description": "An EventTrader account API key (evt_…) from https://cymetica.com/account, or, through an API marketplace, the marketplace's own key: the marketplace adds the subscriber identity header (X-RapidAPI-User / X-Magicapi-User) and each subscriber gets a private workspace."
   },
   "bearer": {
    "type": "http",
    "scheme": "bearer",
    "description": "Authorization: Bearer <account API key or OAuth 2.1 token>"
   }
  },
  "responses": {
   "Error": {
    "description": "Error",
    "content": {
     "application/json": {
      "schema": {
       "$ref": "#/components/schemas/Error"
      }
     }
    }
   },
   "RateLimited": {
    "description": "Too many requests or a plan window exhausted; retry after Retry-After seconds",
    "headers": {
     "Retry-After": {
      "schema": {
       "type": "integer"
      }
     }
    },
    "content": {
     "application/json": {
      "schema": {
       "$ref": "#/components/schemas/Error"
      }
     }
    }
   }
  },
  "schemas": {
   "Example": {
    "type": "object",
    "required": [
     "text",
     "label"
    ],
    "properties": {
     "text": {
      "type": "string",
      "maxLength": 4000
     },
     "label": {
      "type": "string",
      "maxLength": 255
     }
    }
   },
   "CreateClassifier": {
    "type": "object",
    "required": [
     "name",
     "examples"
    ],
    "properties": {
     "name": {
      "type": "string",
      "maxLength": 120
     },
     "examples": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/Example"
      },
      "description": "At least 2 examples per label, up to 255 labels"
     },
     "instructions": {
      "type": "string",
      "maxLength": 4000,
      "description": "What the labels mean (used by the second opinion and a general-model fallback)"
     },
     "threshold": {
      "type": "number",
      "minimum": 0,
      "maximum": 1,
      "default": 0.5,
      "description": "Confidence under which a result is flagged unsure"
     },
     "learn_from_fallback": {
      "type": "boolean",
      "default": true,
      "description": "Add confident fallback answers to the training set"
     }
    }
   },
   "Settings": {
    "type": "object",
    "properties": {
     "name": {
      "type": "string"
     },
     "threshold": {
      "type": "number",
      "minimum": 0,
      "maximum": 1
     },
     "learn_from_fallback": {
      "type": "boolean"
     },
     "instructions": {
      "type": "string"
     }
    }
   },
   "Examples": {
    "type": "object",
    "required": [
     "examples"
    ],
    "properties": {
     "examples": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/Example"
      }
     }
    }
   },
   "Predict": {
    "type": "object",
    "properties": {
     "texts": {
      "type": "array",
      "items": {
       "type": "string",
       "maxLength": 4000
      },
      "maxItems": 256,
      "description": "Up to 256 texts; each counts as one decision"
     },
     "text": {
      "type": "string",
      "description": "A single text (instead of texts)"
     },
     "second_opinion": {
      "type": "boolean",
      "default": true,
      "description": "Re-rank unsure texts with the second-opinion model before the threshold applies"
     }
    }
   },
   "Classifier": {
    "type": "object",
    "properties": {
     "id": {
      "type": "string"
     },
     "name": {
      "type": "string"
     },
     "status": {
      "type": "string",
      "enum": [
       "queued",
       "training",
       "ready",
       "failed"
      ]
     },
     "version": {
      "type": "integer"
     },
     "labels": {
      "type": "array",
      "items": {
       "type": "string"
      }
     },
     "threshold": {
      "type": "number"
     },
     "n_examples": {
      "type": "integer"
     },
     "expected_accuracy": {
      "type": "number",
      "description": "held-out accuracy of the last training run"
     },
     "created_at": {
      "type": "string"
     }
    }
   },
   "ClassifierList": {
    "type": "object",
    "properties": {
     "plan": {
      "type": "string"
     },
     "classifier_limit": {
      "type": "integer"
     },
     "decisions_per_week": {
      "type": "integer"
     },
     "classifiers": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/Classifier"
      }
     }
    }
   },
   "Decision": {
    "type": "object",
    "properties": {
     "label": {
      "type": "string"
     },
     "confidence": {
      "type": "number"
     },
     "unsure": {
      "type": "boolean"
     },
     "source": {
      "type": "string",
      "enum": [
       "specialist",
       "fallback"
      ]
     },
     "second_opinion": {
      "type": "boolean"
     },
     "probabilities": {
      "type": "object",
      "additionalProperties": {
       "type": "number"
      }
     }
    }
   },
   "PredictResponse": {
    "type": "object",
    "properties": {
     "model": {
      "type": "string",
      "example": "tuatara-decide/dc_3f9a@v2"
     },
     "decisions": {
      "type": "integer"
     },
     "fallback_used": {
      "type": "integer"
     },
     "specialist_ms": {
      "type": "number"
     },
     "second_opinions": {
      "type": "integer"
     },
     "results": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/Decision"
      }
     }
    }
   },
   "SystemOne": {
    "type": "object",
    "required": [
     "state",
     "questions"
    ],
    "properties": {
     "model": {
      "type": "string",
      "default": "tuatara/decide-auto",
      "description": "tuatara/decide-auto (default: no classifier needed) or tuatara-decide/<classifier id>"
     },
     "state": {
      "type": "string",
      "description": "The text the questions are about"
     },
     "questions": {
      "type": "object",
      "description": "{id: {type: choice, criteria: {option: what it means, ...}}} (criteria may also be a list of option names); at most 16 questions"
     }
    },
    "example": {
     "model": "tuatara/decide-auto",
     "state": "I still have not received my new card, I ordered over a week ago.",
     "questions": {
      "intent": {
       "type": "choice",
       "criteria": {
        "card_arrival": "asks when an ordered card will arrive",
        "lost_or_stolen_card": "reports a card lost or stolen",
        "card_not_working": "says the card does not work"
       }
      }
     }
    }
   },
   "Error": {
    "type": "object",
    "properties": {
     "detail": {
      "type": "object",
      "properties": {
       "code": {
        "type": "string"
       },
       "message": {
        "type": "string"
       }
      }
     }
    }
   }
  }
 },
 "paths": {
  "/info": {
   "get": {
    "tags": [
     "Info"
    ],
    "operationId": "about",
    "summary": "What Tuatara Decide is: plans, limits, evidence (public)",
    "description": "Name, plan allowances, limits, benchmark evidence links and whether the service is enabled.",
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/Error"
     },
     "403": {
      "$ref": "#/components/responses/Error"
     },
     "404": {
      "$ref": "#/components/responses/Error"
     },
     "422": {
      "$ref": "#/components/responses/Error"
     },
     "429": {
      "$ref": "#/components/responses/RateLimited"
     },
     "503": {
      "$ref": "#/components/responses/Error"
     }
    },
    "security": []
   }
  },
  "/classifiers": {
   "post": {
    "tags": [
     "Classifiers"
    ],
    "operationId": "createClassifier",
    "summary": "Train a classifier from labelled examples",
    "description": "Queues a training run (202). Poll GET /classifiers/{cid} until status is ready (about a minute for small sets). Counts one training against the plan. Marketplace billing: one request.",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/CreateClassifier"
       }
      }
     }
    },
    "responses": {
     "401": {
      "$ref": "#/components/responses/Error"
     },
     "403": {
      "$ref": "#/components/responses/Error"
     },
     "404": {
      "$ref": "#/components/responses/Error"
     },
     "422": {
      "$ref": "#/components/responses/Error"
     },
     "429": {
      "$ref": "#/components/responses/RateLimited"
     },
     "503": {
      "$ref": "#/components/responses/Error"
     },
     "202": {
      "description": "Queued",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Classifier"
        }
       }
      }
     }
    }
   },
   "get": {
    "tags": [
     "Classifiers"
    ],
    "operationId": "listClassifiers",
    "summary": "Your classifiers and plan",
    "description": "Every classifier of the calling account with its plan's classifier limit and weekly decisions.",
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/ClassifierList"
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/Error"
     },
     "403": {
      "$ref": "#/components/responses/Error"
     },
     "404": {
      "$ref": "#/components/responses/Error"
     },
     "422": {
      "$ref": "#/components/responses/Error"
     },
     "429": {
      "$ref": "#/components/responses/RateLimited"
     },
     "503": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/classifiers/upload": {
   "post": {
    "tags": [
     "Classifiers"
    ],
    "operationId": "uploadClassifier",
    "summary": "Train from a CSV upload (text,label columns)",
    "description": "multipart/form-data: file (CSV with text and label columns, up to 50 MB), name, optional instructions and threshold. Queues a training run (202).",
    "requestBody": {
     "required": true,
     "content": {
      "multipart/form-data": {
       "schema": {
        "type": "object",
        "properties": {
         "file": {
          "type": "string",
          "format": "binary"
         },
         "name": {
          "type": "string"
         },
         "instructions": {
          "type": "string"
         },
         "threshold": {
          "type": "number"
         }
        },
        "required": [
         "file",
         "name"
        ]
       }
      }
     }
    },
    "responses": {
     "202": {
      "description": "Queued",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Classifier"
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/Error"
     },
     "403": {
      "$ref": "#/components/responses/Error"
     },
     "413": {
      "$ref": "#/components/responses/Error"
     },
     "422": {
      "$ref": "#/components/responses/Error"
     },
     "429": {
      "$ref": "#/components/responses/RateLimited"
     }
    }
   }
  },
  "/classifiers/{cid}": {
   "get": {
    "tags": [
     "Classifiers"
    ],
    "operationId": "getClassifier",
    "summary": "One classifier: status, labels, expected accuracy",
    "description": "Poll this after creating or retraining.",
    "parameters": [
     {
      "name": "cid",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string"
      },
      "description": "classifier id (dc_…)"
     }
    ],
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Classifier"
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/Error"
     },
     "403": {
      "$ref": "#/components/responses/Error"
     },
     "404": {
      "$ref": "#/components/responses/Error"
     },
     "422": {
      "$ref": "#/components/responses/Error"
     },
     "429": {
      "$ref": "#/components/responses/RateLimited"
     },
     "503": {
      "$ref": "#/components/responses/Error"
     }
    }
   },
   "patch": {
    "tags": [
     "Classifiers"
    ],
    "operationId": "updateClassifier",
    "summary": "Change name, threshold, instructions or fallback learning",
    "description": "",
    "parameters": [
     {
      "name": "cid",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string"
      },
      "description": "classifier id (dc_…)"
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/Settings"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Classifier"
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/Error"
     },
     "403": {
      "$ref": "#/components/responses/Error"
     },
     "404": {
      "$ref": "#/components/responses/Error"
     },
     "422": {
      "$ref": "#/components/responses/Error"
     },
     "429": {
      "$ref": "#/components/responses/RateLimited"
     },
     "503": {
      "$ref": "#/components/responses/Error"
     }
    }
   },
   "delete": {
    "tags": [
     "Classifiers"
    ],
    "operationId": "deleteClassifier",
    "summary": "Delete a classifier, its examples and model files",
    "description": "",
    "parameters": [
     {
      "name": "cid",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string"
      },
      "description": "classifier id (dc_…)"
     }
    ],
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/Error"
     },
     "403": {
      "$ref": "#/components/responses/Error"
     },
     "404": {
      "$ref": "#/components/responses/Error"
     },
     "422": {
      "$ref": "#/components/responses/Error"
     },
     "429": {
      "$ref": "#/components/responses/RateLimited"
     },
     "503": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/classifiers/{cid}/examples": {
   "post": {
    "tags": [
     "Classifiers"
    ],
    "operationId": "addExamples",
    "summary": "Add examples and retrain",
    "description": "Appends labelled examples and queues a new version (202).",
    "parameters": [
     {
      "name": "cid",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string"
      },
      "description": "classifier id (dc_…)"
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/Examples"
       }
      }
     }
    },
    "responses": {
     "401": {
      "$ref": "#/components/responses/Error"
     },
     "403": {
      "$ref": "#/components/responses/Error"
     },
     "404": {
      "$ref": "#/components/responses/Error"
     },
     "422": {
      "$ref": "#/components/responses/Error"
     },
     "429": {
      "$ref": "#/components/responses/RateLimited"
     },
     "503": {
      "$ref": "#/components/responses/Error"
     },
     "202": {
      "description": "Queued",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Classifier"
        }
       }
      }
     }
    }
   }
  },
  "/classifiers/{cid}/predict": {
   "post": {
    "tags": [
     "Decide"
    ],
    "operationId": "predict",
    "summary": "Label up to 256 texts with a ready classifier",
    "description": "Each result has the label, its confidence, every label's probability and unsure=true when confidence is under the classifier's threshold. Optional header X-Fallback-Key: your own general-model key, used only for unsure texts and never stored. Marketplace billing: one unit per text (RapidAPI: Decisions quota object, the plan's request price covers the first; api.market: API units).",
    "parameters": [
     {
      "name": "cid",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string"
      },
      "description": "classifier id (dc_…)"
     },
     {
      "name": "X-Fallback-Key",
      "in": "header",
      "required": false,
      "schema": {
       "type": "string"
      }
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/Predict"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/PredictResponse"
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/Error"
     },
     "403": {
      "$ref": "#/components/responses/Error"
     },
     "404": {
      "$ref": "#/components/responses/Error"
     },
     "422": {
      "$ref": "#/components/responses/Error"
     },
     "429": {
      "$ref": "#/components/responses/RateLimited"
     },
     "503": {
      "$ref": "#/components/responses/Error"
     },
     "409": {
      "description": "The classifier has not finished its first training run",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/classifiers/{cid}/savings": {
   "get": {
    "tags": [
     "Decide"
    ],
    "operationId": "savings",
    "summary": "What this classifier saved versus a general model",
    "description": "Counts of decisions served by the specialist versus the fallback and the estimated cost difference.",
    "parameters": [
     {
      "name": "cid",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string"
      },
      "description": "classifier id (dc_…)"
     }
    ],
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/Error"
     },
     "403": {
      "$ref": "#/components/responses/Error"
     },
     "404": {
      "$ref": "#/components/responses/Error"
     },
     "422": {
      "$ref": "#/components/responses/Error"
     },
     "429": {
      "$ref": "#/components/responses/RateLimited"
     },
     "503": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/systemone": {
   "post": {
    "tags": [
     "Decide"
    ],
    "operationId": "systemone",
    "summary": "One-shot choice without a trained classifier",
    "description": "Answer choice questions about a text in the System One request shape. Leave model out (or set it to tuatara/decide-auto) for Decide Auto: option names alone, no classifier, learns your question as you use it. Set model to tuatara-decide/<classifier id> to answer with your own trained classifier; its options must be that classifier's labels. Marketplace billing: one unit.",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/SystemOne"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/Error"
     },
     "403": {
      "$ref": "#/components/responses/Error"
     },
     "404": {
      "$ref": "#/components/responses/Error"
     },
     "422": {
      "$ref": "#/components/responses/Error"
     },
     "429": {
      "$ref": "#/components/responses/RateLimited"
     },
     "503": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  }
 }
}