{
  "openapi": "3.1.0",
  "info": {
    "title": "My Tree API",
    "version": "1.0.0",
    "summary": "Look up a My Tree by its code, see the gifts on it, leave a gift with a message, report it.",
    "description": "My Tree (decomytree.link) is a free website for the \"Deco My Tree\" trend: people share a virtual Christmas, birthday or Valentine's tree and friends hang gifts with messages on it, which the owner reads on the tree's day. These are the endpoints anyone may call. Send only messages the person you act for wrote or approved; no bulk or unsolicited messages (https://decomytree.link/content-policy). Creating trees and reading messages happen on the website, signed in by the tree owner.",
    "termsOfService": "https://decomytree.link/terms",
    "contact": {
      "name": "My Tree",
      "email": "mytreeapplink@gmail.com",
      "url": "https://decomytree.link/developers"
    }
  },
  "externalDocs": {
    "description": "Developers and AI agents",
    "url": "https://decomytree.link/developers"
  },
  "servers": [
    {
      "url": "https://api.decomytree.link"
    }
  ],
  "paths": {
    "/v1/status": {
      "get": {
        "operationId": "getStatus",
        "summary": "Is My Tree working?",
        "description": "Answers 200 when the service and its database work.",
        "responses": {
          "200": {
            "description": "Working",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "success",
                    "data"
                  ],
                  "properties": {
                    "success": {
                      "const": true
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "status": {
                          "const": "ok"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/trees/{shareCode}": {
      "get": {
        "operationId": "getTree",
        "summary": "Look up a tree",
        "description": "The tree with this code: its name, type, opening day and how many gifts it has.",
        "parameters": [
          {
            "$ref": "#/components/parameters/ShareCode"
          }
        ],
        "responses": {
          "200": {
            "description": "The tree",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "success",
                    "data"
                  ],
                  "properties": {
                    "success": {
                      "const": true
                    },
                    "data": {
                      "$ref": "#/components/schemas/Tree"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The code is not a tree code",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No tree has this code",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests; wait a minute",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/messages/{shareCode}": {
      "get": {
        "operationId": "listGifts",
        "summary": "See the gifts on a tree",
        "description": "The gifts on the tree, oldest first. Only the tree owner can read the messages, so `message` is empty here and a hidden sender name reads \"Anonymous\".",
        "parameters": [
          {
            "$ref": "#/components/parameters/ShareCode"
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "maximum": 10000,
              "default": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of gifts",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "success",
                    "data"
                  ],
                  "properties": {
                    "success": {
                      "const": true
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "messages": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Message"
                          }
                        },
                        "total": {
                          "type": "integer"
                        },
                        "hasMore": {
                          "type": "boolean"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The code is not a tree code",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No tree has this code",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests; wait a minute",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/messages": {
      "post": {
        "operationId": "leaveMessage",
        "summary": "Leave a gift with a message",
        "description": "Hangs a gift with a message on the tree. The owner reads it on the tree’s day and always sees the sender name.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/NewMessage"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Sent; the answer repeats the message",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "success",
                    "data"
                  ],
                  "properties": {
                    "success": {
                      "const": true
                    },
                    "data": {
                      "$ref": "#/components/schemas/Message"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Something is missing or too long",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The tree owner blocked this sender, or the tree is full",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No tree has this code",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "The name or message contains words that are not allowed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many messages; wait a minute",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/reports": {
      "post": {
        "operationId": "reportTree",
        "summary": "Report a tree or a message",
        "description": "A person on the My Tree team reviews every report, as a rule within 48 hours.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Report"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Received",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "success",
                    "data"
                  ],
                  "properties": {
                    "success": {
                      "const": true
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "reported": {
                          "const": true
                        },
                        "id": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Something is missing or invalid",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No tree has this code",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many reports; wait a minute",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "parameters": {
      "ShareCode": {
        "name": "shareCode",
        "in": "path",
        "required": true,
        "description": "The tree code",
        "schema": {
          "$ref": "#/components/schemas/ShareCode"
        }
      }
    },
    "schemas": {
      "ShareCode": {
        "type": "string",
        "pattern": "^[A-HJ-NP-Z2-9]{8}$",
        "description": "8 letters and digits (no I, O, 0 or 1), as in https://decomytree.link/send-message?tree=ABCD2345",
        "examples": [
          "ABCD2345"
        ]
      },
      "Tree": {
        "type": "object",
        "properties": {
          "treeName": {
            "type": "string",
            "maxLength": 30
          },
          "shareCode": {
            "$ref": "#/components/schemas/ShareCode"
          },
          "occasion": {
            "type": "string",
            "enum": [
              "christmas",
              "birthday",
              "valentine"
            ]
          },
          "opensOn": {
            "type": "string",
            "format": "date",
            "description": "The day the messages open: December 25, February 14 or the owner’s birthday"
          },
          "messageCount": {
            "type": "integer",
            "description": "Gifts on the tree"
          },
          "treeAssetId": {
            "type": "string",
            "description": "Tree design"
          },
          "headerAssetId": {
            "type": "string",
            "description": "Tree topper"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "lastMessageAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "Message": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "shareCode": {
            "$ref": "#/components/schemas/ShareCode"
          },
          "position": {
            "type": "integer",
            "description": "Place on the tree, in order of arrival"
          },
          "senderName": {
            "type": "string",
            "description": "\"Anonymous\" when the sender hid their name from visitors"
          },
          "message": {
            "type": "string",
            "description": "Empty unless you are the tree owner (or the sender, in the answer to sending)"
          },
          "giftAssetId": {
            "$ref": "#/components/schemas/GiftId"
          },
          "showSenderName": {
            "type": "boolean"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "NewMessage": {
        "type": "object",
        "required": [
          "shareCode",
          "senderName",
          "message",
          "giftAssetId"
        ],
        "properties": {
          "shareCode": {
            "$ref": "#/components/schemas/ShareCode"
          },
          "senderName": {
            "type": "string",
            "minLength": 1,
            "maxLength": 30,
            "description": "The tree owner always sees it"
          },
          "message": {
            "type": "string",
            "minLength": 1,
            "maxLength": 500
          },
          "giftAssetId": {
            "$ref": "#/components/schemas/GiftId"
          },
          "showSenderName": {
            "type": "boolean",
            "default": true,
            "description": "false hides the name from other visitors, not from the owner"
          }
        }
      },
      "GiftId": {
        "type": "string",
        "description": "The gift to hang. The website offers each tree type its own selection (Christmas, birthday or Valentine’s gifts plus the ones for every tree).",
        "enum": [
          "card",
          "crown",
          "chocolate-heart",
          "rose",
          "love-letter",
          "sealed-letter",
          "kiss",
          "heart-hands",
          "birthday-bear",
          "party-glasses",
          "lollipop",
          "rainbow",
          "wrapped-candy",
          "heart-chocolate-box",
          "chocolate-box",
          "heart-cake",
          "heart-padlock",
          "heart-key",
          "heart-puzzle",
          "heart-pillow",
          "love-magnet",
          "love-match",
          "cat-pillow",
          "perfume",
          "cologne",
          "wine-glasses",
          "champagne-glasses",
          "champagne",
          "party-bear",
          "birthday-cat",
          "unicorn",
          "dino",
          "bunny",
          "monkey",
          "doll",
          "toy-car",
          "disco-ball",
          "ornament-star",
          "ornament-gold-gift",
          "ornament-red-gift",
          "ornament-purple-gift",
          "ornament-green-gift",
          "ornament-chocolate",
          "ornament-cake",
          "ornament-candy",
          "ornament-clover",
          "ornament-lantern",
          "ornament-snowman",
          "ornament-snow-globe",
          "ornament-candy-cane",
          "ornament-wreath",
          "ornament-reindeer",
          "ornament-santa-hat",
          "ornament-red-stocking",
          "ornament-green-stocking",
          "ornament-blue-stocking",
          "christmas-mug",
          "Gaming-mouse",
          "Gaming-mousepad-RGB",
          "Gaming-mousepad-RGB2",
          "Lego-puzzle1",
          "Lego-puzzle2",
          "jigsaw-puzzle",
          "VR-glasses-Meta-Quest",
          "Wireless-gaming-headset",
          "Xbox-Game-Pass",
          "Exploding-Kittens-card-game",
          "Dixit-board-game",
          "Retro-mini-game-console2",
          "Retro-mini-game-console",
          "RGB-LED-light-strip",
          "RGB-LED-light-strip2",
          "RGB-LED-light-strip3",
          "figurine1",
          "figurine2",
          "robot",
          "drone",
          "black-skates",
          "purple-skates",
          "blue-bear",
          "green-bear",
          "purple-bear",
          "yellow-bear",
          "pink-teddy",
          "rabbit-teddy",
          "deer-teddy",
          "riding-ant",
          "funko-pop",
          "bluephone",
          "whitephone",
          "smartwatch",
          "headphones",
          "plane-ticket",
          "plane-ticket1",
          "glove",
          "snow-globe",
          "sock"
        ]
      },
      "Report": {
        "type": "object",
        "required": [
          "shareCode",
          "reasons"
        ],
        "properties": {
          "shareCode": {
            "$ref": "#/components/schemas/ShareCode"
          },
          "messageId": {
            "type": "string",
            "maxLength": 80,
            "description": "Leave out to report the tree itself"
          },
          "reasons": {
            "type": "array",
            "minItems": 1,
            "maxItems": 5,
            "items": {
              "type": "string",
              "enum": [
                "annoying",
                "offensive",
                "spam",
                "inappropriate",
                "threat",
                "other"
              ]
            }
          },
          "details": {
            "type": "string",
            "maxLength": 500
          }
        }
      },
      "Error": {
        "type": "object",
        "required": [
          "success",
          "error"
        ],
        "properties": {
          "success": {
            "const": false
          },
          "error": {
            "type": "string"
          },
          "code": {
            "type": "string"
          }
        }
      }
    }
  }
}
