{
  "openapi": "3.1.0",
  "info": {
    "title": "Client tokens",
    "version": "2026-10-08",
    "description": "Issue scoped client tokens for browsers and mobile apps. Currently supports feature flag evaluation only."
  },
  "servers": [
    {
      "url": "https://api.motor.dev/meta"
    }
  ],
  "security": [
    {
      "SecretKey": []
    }
  ],
  "tags": [
    {
      "name": "Client tokens"
    }
  ],
  "paths": {
    "/client_tokens": {
      "post": {
        "operationId": "issueClientToken",
        "tags": [
          "Client tokens"
        ],
        "summary": "Issue a client token",
        "description": "Issues a short-lived token that evaluates chosen client-visible flags for one subject in a browser or app. Your server authenticates the user first. A publishable key can issue only anonymous tokens. Credentials: a secret key, a publishable key.",
        "parameters": [
          {
            "$ref": "#/components/parameters/MotorMode"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "subject": {
                    "type": "string",
                    "description": "The subject's targeting key. Required for a secret key; a publishable key gets an anonymous one.",
                    "minLength": 1,
                    "maxLength": 256
                  },
                  "scopes": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "feature_flags:evaluate"
                      ]
                    },
                    "minItems": 1,
                    "maxItems": 1
                  },
                  "expires_in": {
                    "type": "integer",
                    "description": "Seconds: 60 to 86,400. Default 900.",
                    "minimum": 60,
                    "maximum": 86400
                  },
                  "feature_flags": {
                    "type": "object",
                    "properties": {
                      "environment_id": {
                        "type": "string"
                      },
                      "publishable_key_id": {
                        "type": "string"
                      },
                      "flag_keys": {
                        "type": "array",
                        "items": {
                          "$ref": "#/components/schemas/FlagKey"
                        },
                        "minItems": 1,
                        "maxItems": 100,
                        "uniqueItems": true
                      },
                      "attributes": {
                        "type": "object"
                      },
                      "strategy_results": {
                        "type": "object",
                        "additionalProperties": {
                          "type": "boolean"
                        }
                      }
                    },
                    "required": [
                      "environment_id",
                      "publishable_key_id",
                      "flag_keys"
                    ],
                    "additionalProperties": false
                  }
                },
                "required": [
                  "scopes",
                  "feature_flags"
                ],
                "additionalProperties": false
              },
              "example": {
                "subject": "user_123",
                "scopes": [
                  "feature_flags:evaluate"
                ],
                "expires_in": 900,
                "feature_flags": {
                  "environment_id": "ffe_UJAs9eZep7hvba6xBh5dvY",
                  "publishable_key_id": "key_DuQaWzydzQjd8J84rbPfSd",
                  "flag_keys": [
                    "checkout"
                  ],
                  "attributes": {
                    "plan": "pro"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Success.",
            "headers": {
              "Location": {
                "description": "The URL of the new resource.",
                "schema": {
                  "type": "string"
                }
              },
              "ETag": {
                "description": "The ETag identifying this resource or configuration revision. Use it for conditional requests.",
                "schema": {
                  "type": "string"
                }
              },
              "Idempotency-Replayed": {
                "description": "`true` when this is a replay of a previously recorded response.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ClientToken"
                },
                "example": {
                  "id": "ct_RuAcjb8WxC7DHMftuaGCiz",
                  "token": "motor_ct_EXAMPLE_NOT_A_REAL_SECRET",
                  "subject": "user_123",
                  "expires_at": "2026-10-05T00:16:39Z"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "422": {
            "$ref": "#/components/responses/Error422"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          }
        },
        "security": [
          {
            "SecretKey": []
          },
          {
            "PublishableKey": []
          }
        ]
      }
    },
    "/client_tokens/{token_id}": {
      "delete": {
        "operationId": "revokeClientToken",
        "tags": [
          "Client tokens"
        ],
        "summary": "Revoke a client token",
        "description": "Immediately revokes a client token. You can authenticate with the token itself to revoke it. Credentials: a secret key, a client token.",
        "parameters": [
          {
            "name": "token_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/MotorMode"
          }
        ],
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Deleted"
                },
                "example": {
                  "id": "ct_RuAcjb8WxC7DHMftuaGCiz",
                  "deleted": true
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          }
        },
        "security": [
          {
            "SecretKey": []
          },
          {
            "ClientToken": []
          }
        ]
      }
    }
  },
  "components": {
    "securitySchemes": {
      "SecretKey": {
        "type": "http",
        "scheme": "bearer",
        "description": "A Motor secret key, `motor_sk_...`. Keep it on your server."
      },
      "PublishableKey": {
        "type": "http",
        "scheme": "bearer",
        "description": "The publishable half of a Motor key, `motor_pk_...`, for browsers and apps. It sees only client-visible flags, with anonymous `anon:<UUID v4>` subjects."
      },
      "ClientToken": {
        "type": "http",
        "scheme": "bearer",
        "description": "A client token, `motor_ct_...`, that your server issues for one subject and chosen flags."
      }
    },
    "parameters": {
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": true,
        "description": "1 to 128 printable ASCII characters that you choose for this operation. A retry with the same key and body returns the recorded response for 35 days, with `Idempotency-Replayed: true`; another body with the same key is `409`. Use a new key for a new operation.",
        "schema": {
          "type": "string",
          "minLength": 1,
          "maxLength": 128
        }
      },
      "MotorMode": {
        "name": "Motor-Mode",
        "in": "header",
        "required": false,
        "description": "The feature flags API does not support test mode. Omit this header or set it to `live`. Requests with `test` are rejected before processing or billing. Use a separate environment, such as staging, for tests.",
        "schema": {
          "type": "string",
          "enum": [
            "live"
          ]
        }
      }
    },
    "responses": {
      "Error401": {
        "description": "The credential is missing, invalid, expired, or revoked.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "Error403": {
        "description": "The credential can't do this here, or the account is suspended or closed.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "Error404": {
        "description": "The resource doesn't exist, or belongs to another team.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "Error422": {
        "description": "A field isn't valid. The message names its JSON path.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "Error429": {
        "description": "Too many requests. `Retry-After` gives the seconds to wait.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        },
        "headers": {
          "Retry-After": {
            "schema": {
              "type": "integer"
            }
          }
        }
      }
    },
    "schemas": {
      "FlagKey": {
        "type": "string",
        "description": "A key of a flag, segment, variation, audience, experiment, or tag: a letter or digit, then letters, digits, `_`, `.` and `-`, at most 128 characters.",
        "pattern": "^[A-Za-z0-9][A-Za-z0-9_.-]{0,127}$"
      },
      "Problem": {
        "type": "object",
        "description": "An error. The response has no other data.",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "description": "A stable code, such as `invalid_field`, `not_found`, `revision_conflict`, `resource_in_use`, `limit_reached`, or `rate_limited`."
              },
              "message": {
                "type": "string",
                "description": "What went wrong and what to do, in English."
              },
              "references": {
                "type": "array",
                "description": "For `resource_in_use`: up to 20 keys of resources that refer to this one.",
                "items": {
                  "type": "string"
                }
              },
              "has_more": {
                "type": "boolean",
                "description": "For `resource_in_use`: whether more resources refer to this one."
              }
            },
            "required": [
              "code",
              "message"
            ],
            "additionalProperties": false
          }
        },
        "required": [
          "error"
        ],
        "additionalProperties": false
      },
      "Deleted": {
        "type": "object",
        "description": "A deleted resource.",
        "properties": {
          "id": {
            "type": "string"
          },
          "deleted": {
            "const": true
          }
        },
        "required": [
          "id",
          "deleted"
        ],
        "additionalProperties": false
      },
      "ClientToken": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "token": {
            "type": "string"
          },
          "subject": {
            "type": "string"
          },
          "expires_at": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "token",
          "subject",
          "expires_at"
        ],
        "additionalProperties": false
      }
    }
  }
}
