{
  "openapi": "3.1.0",
  "info": {
    "title": "Arttend agent diary API",
    "version": "1.0.0",
    "description": "Resolve an exact event/session before writing to the key owner’s diary. Human permission required. No invented dates, scores or reviews. See /agents."
  },
  "servers": [
    {
      "url": "https://arttend.com"
    }
  ],
  "components": {
    "securitySchemes": {
      "AgentKey": {
        "type": "http",
        "scheme": "bearer",
        "description": "Revocable art_ key generated in the human’s profile. Expires in 90 days."
      }
    }
  },
  "paths": {
    "/api/agent/search": {
      "get": {
        "operationId": "resolveEvent",
        "security": [
          {
            "AgentKey": []
          }
        ],
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 2,
              "maxLength": 160
            }
          },
          {
            "name": "date",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "city",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Artists, session candidates and clarification questions."
          },
          "401": {
            "description": "Missing, expired or revoked agent key."
          },
          "429": {
            "description": "Agent request quota reached. Respect Retry-After and the daily budget."
          }
        }
      }
    },
    "/api/agent/log": {
      "post": {
        "operationId": "logAttendedEvent",
        "security": [
          {
            "AgentKey": []
          }
        ],
        "parameters": [
          {
            "name": "X-Arttend-Confirmed",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "true"
              ]
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "slug",
                  "sessionId",
                  "confirmed"
                ],
                "properties": {
                  "slug": {
                    "type": "string"
                  },
                  "sessionId": {
                    "type": "integer",
                    "minimum": 1
                  },
                  "confirmed": {
                    "type": "boolean",
                    "const": true
                  },
                  "rating": {
                    "type": "number",
                    "minimum": 0,
                    "maximum": 5,
                    "multipleOf": 0.5
                  },
                  "review": {
                    "type": "string",
                    "maxLength": 2000
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Visit saved. Omitted rating/review preserve existing feedback."
          },
          "400": {
            "description": "Invalid or future session, ambiguous/missing identity or invalid feedback."
          },
          "401": {
            "description": "Missing, expired or revoked agent key."
          },
          "404": {
            "description": "Session not found."
          },
          "429": {
            "description": "Agent request quota reached. Respect Retry-After and the daily budget."
          },
          "428": {
            "description": "Missing explicit human-authorization confirmation header."
          }
        }
      }
    },
    "/api/events": {
      "post": {
        "operationId": "createMissingEvent",
        "security": [
          {
            "AgentKey": []
          }
        ],
        "parameters": [
          {
            "name": "X-Arttend-Confirmed",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "true"
              ]
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name",
                  "category",
                  "venue",
                  "city",
                  "country",
                  "date"
                ],
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "category": {
                    "type": "string",
                    "enum": [
                      "Concerto",
                      "Festival",
                      "Teatro",
                      "Musical",
                      "Stand-up",
                      "Dança",
                      "Outro"
                    ]
                  },
                  "artistId": {
                    "type": "integer"
                  },
                  "performer": {
                    "type": "string"
                  },
                  "venue": {
                    "type": "string"
                  },
                  "city": {
                    "type": "string"
                  },
                  "country": {
                    "type": "string"
                  },
                  "locationId": {
                    "type": "integer",
                    "description": "Required for standalone events. GET /api/location/cities returns this id; copy city/country exactly."
                  },
                  "date": {
                    "type": "string",
                    "format": "date"
                  },
                  "time": {
                    "type": "string"
                  },
                  "festivalSlug": {
                    "type": "string"
                  },
                  "festivalSessionId": {
                    "type": "integer"
                  },
                  "createOnly": {
                    "type": "boolean"
                  },
                  "rating": {
                    "type": "number",
                    "minimum": 0,
                    "maximum": 5,
                    "multipleOf": 0.5
                  },
                  "review": {
                    "type": "string",
                    "maxLength": 2000
                  }
                }
              }
            }
          }
        },
        "responses": {
          "400": {
            "description": "Missing/invalid verified location or artist identity. Follow returned clarification."
          },
          "401": {
            "description": "Missing, expired or revoked agent key."
          },
          "409": {
            "description": "Duplicate exists; use the returned event/session."
          },
          "201": {
            "description": "Created using the same validation as the application."
          },
          "429": {
            "description": "Agent request quota reached. Respect Retry-After and the daily budget."
          },
          "428": {
            "description": "Missing explicit human-authorization confirmation header."
          }
        }
      }
    },
    "/api/artists": {
      "get": {
        "operationId": "findArtists",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Catalogue artist identities."
          }
        }
      },
      "post": {
        "operationId": "createMissingArtist",
        "security": [
          {
            "AgentKey": []
          }
        ],
        "parameters": [
          {
            "name": "X-Arttend-Confirmed",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "true"
              ]
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name",
                  "country",
                  "genre",
                  "disambiguation"
                ],
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "country": {
                    "type": "string",
                    "description": "ISO 3166-1 alpha-2 country code, e.g. GB"
                  },
                  "genre": {
                    "type": "string"
                  },
                  "disambiguation": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Artist identity created."
          },
          "400": {
            "description": "Missing disambiguation."
          },
          "409": {
            "description": "Existing identity."
          },
          "201": {
            "description": "New artist identity created."
          },
          "429": {
            "description": "Agent request quota reached. Respect Retry-After and the daily budget."
          },
          "428": {
            "description": "Missing explicit human-authorization confirmation header."
          }
        }
      }
    },
    "/api/location/cities": {
      "get": {
        "operationId": "findVerifiedCity",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 2,
              "maxLength": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Verified locationId, city, country and region. Use these exact fields when creating an event."
          }
        }
      }
    },
    "/api/events/{slug}/attendance": {
      "get": {
        "summary": "Read a day-level memory without assuming a session",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "scope",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "day"
              ]
            }
          },
          {
            "name": "date",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "date"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Day-level memory"
          },
          "400": {
            "description": "Invalid date or feedback"
          },
          "401": {
            "description": "Authentication required"
          },
          "404": {
            "description": "No programme on that date"
          },
          "409": {
            "description": "Concrete visit already exists; edit it instead"
          },
          "428": {
            "description": "Confirmation header required for writing"
          },
          "429": {
            "description": "Quota reached"
          }
        }
      },
      "post": {
        "summary": "Log the confirmed day when the human cannot recall the session",
        "description": "Use action=attended when no feedback is requested. For action=review first read the memory and preserve existing rating/review values not changed by the human. Do not invent a session.",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Arttend-Confirmed",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "true"
              ]
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "scope",
                  "diaryDate",
                  "action"
                ],
                "properties": {
                  "scope": {
                    "type": "string",
                    "enum": [
                      "day"
                    ]
                  },
                  "diaryDate": {
                    "type": "string",
                    "format": "date"
                  },
                  "action": {
                    "type": "string",
                    "enum": [
                      "attended",
                      "review"
                    ]
                  },
                  "rating": {
                    "type": "number",
                    "minimum": 0,
                    "maximum": 5,
                    "multipleOf": 0.5
                  },
                  "review": {
                    "type": "string",
                    "maxLength": 2000
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Day-level memory"
          },
          "400": {
            "description": "Invalid date or feedback"
          },
          "401": {
            "description": "Authentication required"
          },
          "404": {
            "description": "No programme on that date"
          },
          "409": {
            "description": "Concrete visit already exists; edit it instead"
          },
          "428": {
            "description": "Confirmation header required for writing"
          },
          "429": {
            "description": "Quota reached"
          }
        }
      }
    }
  }
}
