{
  "openapi": "3.1.0",
  "info": {
    "title": "Meta API",
    "version": "2026-10-22",
    "description": "What every Motor service shares. Each team has a stream of events: what happened in the team, in Motor and in the other services. Motor keeps 30 days of it.\n\nMotor's events: `motor.team.created`, `motor.team.updated`, `motor.team.deleted`, `motor.team.card_saved`, `motor.team.suspended`, `motor.team.unsuspended`, `motor.member.added`, `motor.member.updated`, `motor.member.removed`, `motor.invite.created`, `motor.invite.deleted`, `motor.agent.started`, `motor.agent.ended`, `motor.invoice.created`, `motor.invoice.paid`, `motor.invoice.payment_failed`, and `motor.spending.limit_reached`."
  },
  "servers": [
    {
      "url": "https://new.motor.dev"
    }
  ],
  "security": [
    {
      "bearer": []
    },
    {
      "dpop": []
    }
  ],
  "tags": [
    {
      "name": "Events",
      "description": "The stream of a team's events."
    }
  ],
  "paths": {
    "/api/meta/events": {
      "get": {
        "operationId": "list_events",
        "summary": "List events",
        "description": "Returns a team's events after the event `after`, oldest first, for up to 30 days back. Poll with `next_cursor` as `after`, or stream instead. Needs `events:read` for an agent.",
        "tags": [
          "Events"
        ],
        "responses": {
          "200": {
            "description": "A page of events.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EventPage"
                },
                "example": {
                  "data": [
                    {
                      "id": "evt_1BbA6sXxyUWHYvKqN5T3dM",
                      "type": "motor.agent.started",
                      "team_id": "team_1BbA6sXxyUWHYvKqN5T3dM",
                      "data": {
                        "agent_id": "agent_1BbA6sXxyUWHYvKqN5T3dM",
                        "name": "reviewer",
                        "scopes": [
                          "current:*"
                        ],
                        "user_id": "user_1BbA6sXxyUWHYvKqN5T3dM",
                        "expires_at": "2026-10-23T09:00:00Z"
                      },
                      "created_at": "2026-10-22T09:00:00.000Z"
                    }
                  ],
                  "next_cursor": "evt_1BbA6sXxyUWHYvKqN5T3dM",
                  "has_more": false
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/InvalidField"
          }
        },
        "parameters": [
          {
            "name": "team_id",
            "in": "query",
            "required": false,
            "description": "The team's ID. An agent token reads its own team's stream if you don't say.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "after",
            "in": "query",
            "required": false,
            "description": "The ID of the last event that you have. The start of the stream if you don't say.",
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/Limit"
          }
        ]
      }
    },
    "/api/meta/events/stream": {
      "get": {
        "operationId": "stream_events",
        "summary": "Stream events",
        "description": "Streams a team's events as server-sent events, from after the event in `Last-Event-ID` or `after`, or from now. Each event is `id: <event ID>`, `event: <type>`, and `data: <the event as JSON>`. A comment comes every 25 seconds. A stream ends after an hour; connect again with the ID of the last event that you got. Needs `events:read` for an agent.",
        "tags": [
          "Events"
        ],
        "responses": {
          "200": {
            "description": "The stream.",
            "content": {
              "text/event-stream": {
                "schema": {
                  "type": "string"
                },
                "example": "id: evt_1BbA6sXxyUWHYvKqN5T3dM\nevent: motor.agent.started\ndata: {\"id\":\"evt_1BbA6sXxyUWHYvKqN5T3dM\",\"type\":\"motor.agent.started\",\"team_id\":\"team_1BbA6sXxyUWHYvKqN5T3dM\",\"data\":{\"agent_id\":\"agent_1BbA6sXxyUWHYvKqN5T3dM\"},\"created_at\":\"2026-10-22T09:00:00.000Z\"}\n\n"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/InvalidField"
          }
        },
        "parameters": [
          {
            "name": "team_id",
            "in": "query",
            "required": false,
            "description": "The team's ID. An agent token reads its own team's stream if you don't say.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "after",
            "in": "query",
            "required": false,
            "description": "The ID of the last event that you have.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Last-Event-ID",
            "in": "header",
            "required": false,
            "description": "The ID of the last event that you got; browsers send it when they connect again.",
            "schema": {
              "type": "string"
            }
          }
        ]
      }
    },
    "/api/meta/events/{event_id}": {
      "get": {
        "operationId": "get_event",
        "summary": "Get an event",
        "description": "Returns one event of a team's stream.",
        "tags": [
          "Events"
        ],
        "responses": {
          "200": {
            "description": "The event.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Event"
                },
                "example": {
                  "id": "evt_1BbA6sXxyUWHYvKqN5T3dM",
                  "type": "motor.agent.started",
                  "team_id": "team_1BbA6sXxyUWHYvKqN5T3dM",
                  "data": {
                    "agent_id": "agent_1BbA6sXxyUWHYvKqN5T3dM",
                    "name": "reviewer",
                    "scopes": [
                      "current:*"
                    ],
                    "user_id": "user_1BbA6sXxyUWHYvKqN5T3dM",
                    "expires_at": "2026-10-23T09:00:00Z"
                  },
                  "created_at": "2026-10-22T09:00:00.000Z"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/InvalidField"
          }
        },
        "parameters": [
          {
            "name": "event_id",
            "in": "path",
            "required": true,
            "description": "The event's ID.",
            "schema": {
              "type": "string",
              "pattern": "^evt_"
            }
          },
          {
            "name": "team_id",
            "in": "query",
            "required": false,
            "description": "The team's ID. An agent token reads its own team's stream if you don't say.",
            "schema": {
              "type": "string"
            }
          }
        ]
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearer": {
        "type": "http",
        "scheme": "bearer",
        "description": "A session key (`motor_sk_`), a user key (`motor_uk_`), or an agent token (`motor_tk_`)."
      },
      "dpop": {
        "type": "http",
        "scheme": "DPoP",
        "description": "A session key that is bound to a device key, with a `DPoP` header that holds a proof of the device key (RFC 9449)."
      }
    },
    "parameters": {
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": false,
        "description": "A key, 1 to 128 printable characters, that makes the request happen once: for 24 hours, the same key and request get the first answer again.",
        "schema": {
          "type": "string",
          "minLength": 1,
          "maxLength": 128
        }
      },
      "TeamID": {
        "name": "team_id",
        "in": "path",
        "required": true,
        "description": "The team's ID.",
        "schema": {
          "type": "string",
          "pattern": "^team_"
        }
      },
      "Limit": {
        "name": "limit",
        "in": "query",
        "required": false,
        "description": "The most items in the page, from 1 to 100. 50 if you don't say.",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 100
        }
      },
      "Cursor": {
        "name": "cursor",
        "in": "query",
        "required": false,
        "description": "The `next_cursor` of the last page.",
        "schema": {
          "type": "string"
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "The body isn't a JSON object, or the request can't go on.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": {
                "code": "invalid_json",
                "message": "The body isn't a JSON object: unexpected EOF.",
                "retryable": false
              }
            }
          }
        }
      },
      "Unauthorized": {
        "description": "The credential is missing, unknown, revoked, or expired, or a bound session key has no valid DPoP proof.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": {
                "code": "unauthorized",
                "message": "The credential is unknown, revoked, or expired.",
                "retryable": false
              }
            }
          }
        }
      },
      "BillingRequired": {
        "description": "The team needs a card before it can do billable work.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": {
                "code": "billing_required",
                "message": "The team needs a card before it can do billable work.",
                "retryable": false
              }
            }
          }
        }
      },
      "Forbidden": {
        "description": "The credential can't do this.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": {
                "code": "forbidden",
                "message": "Your credential can't do this.",
                "retryable": false
              }
            }
          }
        }
      },
      "NotFound": {
        "description": "There is no such resource, or the credential can't see it.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": {
                "code": "not_found",
                "message": "There is no such resource, or you can't see it.",
                "retryable": false
              }
            }
          }
        }
      },
      "Conflict": {
        "description": "The state of things doesn't allow the request, or an Idempotency-Key was used with another request.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": {
                "code": "conflict",
                "message": "A team needs an admin. Make another member an admin first.",
                "retryable": false
              }
            }
          }
        }
      },
      "InvalidField": {
        "description": "A field or a parameter has a value that Motor can't accept.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": {
                "code": "invalid_field",
                "message": "`name` is required.",
                "retryable": false
              }
            }
          }
        }
      },
      "RateLimited": {
        "description": "The credential made more than 600 requests in a minute.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": {
                "code": "rate_limited",
                "message": "Too many requests: the limit is 600 a minute.",
                "retryable": true
              }
            }
          }
        }
      }
    },
    "schemas": {
      "Event": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The event's ID, which is also its place in the stream."
          },
          "type": {
            "type": "string",
            "description": "What happened, as `<service>.<resource>.<verb>`, such as `motor.agent.started`."
          },
          "team_id": {
            "type": "string",
            "description": "The team's ID."
          },
          "data": {
            "type": "object",
            "description": "The event's fields, which depend on its type."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "type",
          "team_id",
          "data",
          "created_at"
        ],
        "additionalProperties": false,
        "title": "Event"
      },
      "EventPage": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Event"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "The ID of the last event of the page, for `after`; the given `after` when the page is empty; null when the stream is empty."
          },
          "has_more": {
            "type": "boolean",
            "description": "Whether more events follow now."
          }
        },
        "required": [
          "data",
          "next_cursor",
          "has_more"
        ],
        "additionalProperties": false
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "description": "What went wrong, for programs, such as `invalid_field`."
              },
              "message": {
                "type": "string",
                "description": "What went wrong, for people."
              },
              "field": {
                "type": "string",
                "description": "The field with the problem, for `invalid_field`."
              },
              "retryable": {
                "type": "boolean",
                "description": "Whether the same request can succeed later."
              },
              "retry_after_seconds": {
                "type": "integer",
                "description": "How long to wait before a retry."
              }
            },
            "required": [
              "code",
              "message",
              "retryable"
            ],
            "additionalProperties": false
          }
        },
        "required": [
          "error"
        ],
        "additionalProperties": false,
        "title": "Error"
      }
    }
  }
}
