{
  "$schema": "https://plus.solberg.is/schema/manifest.json",
  "site": "noona.is",
  "title": "Noona (noona.is)",
  "description": "Iceland's booking platform: search businesses, list services, check availability, view your bookings — and book/cancel with confirmation and a prepayment guard.",
  "version": "0.2.0",
  "author": "jokull <https://github.com/jokull>",
  "license": "MIT",
  "tags": [
    "bookings",
    "appointments",
    "health"
  ],
  "identities": [
    "customer"
  ],
  "auth": [
    {
      "id": "sms-otp",
      "type": "otp-token",
      "flow": "cli",
      "summary": "Phone + SMS code → long-lived JWT (keychain, never plaintext)",
      "default": true,
      "probe": "GET /user returns the profile",
      "credentials": [
        "NOONA_TOKEN"
      ],
      "steps": [
        {
          "kind": "prompt",
          "id": "phone",
          "prompt": "Phone number (+354 …)",
          "pattern": "^\\+?[0-9][0-9\\s-]{5,17}$",
          "assign": "phone"
        },
        {
          "kind": "compute",
          "id": "split",
          "fn": "splitPhone",
          "args": [
            {
              "$": "phone"
            }
          ],
          "assign": "p"
        },
        {
          "kind": "request",
          "id": "send",
          "method": "POST",
          "url": "https://api.noona.is/v1/marketplace/user/verify_phone_number",
          "body": {
            "phone_country_code": {
              "$": "p.cc"
            },
            "phone_number": {
              "$": "p.num"
            }
          },
          "expect": [
            200,
            201
          ]
        },
        {
          "kind": "prompt",
          "id": "otp",
          "prompt": "Enter the SMS code",
          "assign": "otp"
        },
        {
          "kind": "request",
          "id": "verify",
          "method": "POST",
          "url": "https://api.noona.is/v1/marketplace/user/verify_phone_number",
          "body": {
            "phone_country_code": {
              "$": "p.cc"
            },
            "phone_number": {
              "$": "p.num"
            },
            "otp": {
              "$": "otp"
            }
          },
          "expect": [
            200,
            201
          ],
          "extract": {
            "token": "token ?? access_token ?? jwt"
          }
        },
        {
          "kind": "store",
          "id": "store",
          "credential": "NOONA_TOKEN",
          "value": {
            "$": "token"
          }
        },
        {
          "kind": "request",
          "id": "check",
          "method": "GET",
          "url": "https://api.noona.is/v1/marketplace/user",
          "headers": {
            "Authorization": {
              "$": "NOONA_TOKEN"
            }
          },
          "expect": [
            200
          ]
        }
      ]
    }
  ],
  "ops": [
    {
      "id": "search",
      "kind": "rest",
      "title": "Search businesses",
      "description": "Search enterprises by name/query.",
      "tags": [
        "search"
      ],
      "risk": "read-only",
      "input": {
        "type": "object",
        "properties": {
          "query": {
            "type": "string"
          }
        },
        "required": [
          "query"
        ]
      },
      "output": {
        "$ref": "#/schemas/Enterprise"
      }
    },
    {
      "id": "services",
      "kind": "rest",
      "title": "List services",
      "description": "Event types (services) for a company, with price and prepayment requirements.",
      "tags": [
        "services"
      ],
      "risk": "read-only",
      "input": {
        "type": "object",
        "properties": {
          "companyId": {
            "type": "string"
          }
        },
        "required": [
          "companyId"
        ]
      },
      "output": {
        "$ref": "#/schemas/EventType"
      }
    },
    {
      "id": "availability",
      "kind": "rest",
      "title": "Check availability",
      "description": "Time slots for a company + event type over a date range.",
      "tags": [
        "availability"
      ],
      "risk": "read-only",
      "input": {
        "type": "object",
        "properties": {
          "companyId": {
            "type": "string"
          },
          "eventTypeId": {
            "type": "string"
          },
          "startDate": {
            "type": "string",
            "format": "date"
          },
          "days": {
            "type": "integer",
            "minimum": 1,
            "maximum": 30,
            "default": 7
          }
        },
        "required": [
          "companyId",
          "eventTypeId"
        ]
      },
      "output": {
        "$ref": "#/schemas/TimeSlotDay"
      }
    },
    {
      "id": "bookings",
      "kind": "rest",
      "title": "My bookings",
      "description": "Upcoming bookings (events) with service, company, employee, price and status.",
      "tags": [
        "bookings"
      ],
      "risk": "read-only",
      "input": {
        "type": "object",
        "properties": {
          "limit": {
            "type": "integer",
            "minimum": 1,
            "maximum": 100,
            "default": 20
          }
        }
      },
      "output": {
        "$ref": "#/schemas/Booking"
      }
    },
    {
      "id": "book",
      "kind": "rest",
      "title": "Book a slot",
      "description": "Two-step: create time_slot_reservation, then confirm into an event. Refuses services that require prepayment (avoids accidental charges). Mutating.",
      "tags": [
        "bookings"
      ],
      "risk": "mutating",
      "confirm": true,
      "idempotent": false,
      "dryRun": true,
      "input": {
        "type": "object",
        "properties": {
          "companyId": {
            "type": "string"
          },
          "eventTypeId": {
            "type": "string"
          },
          "startsAt": {
            "type": "string",
            "format": "date-time"
          },
          "employeeId": {
            "type": "string"
          }
        },
        "required": [
          "companyId",
          "eventTypeId",
          "startsAt"
        ]
      },
      "output": {
        "$ref": "#/schemas/Booking"
      }
    },
    {
      "id": "cancel",
      "kind": "rest",
      "title": "Cancel a booking",
      "description": "Cancel an event by id. Mutating.",
      "tags": [
        "bookings"
      ],
      "risk": "mutating",
      "confirm": true,
      "idempotent": true,
      "input": {
        "type": "object",
        "properties": {
          "eventId": {
            "type": "string"
          }
        },
        "required": [
          "eventId"
        ]
      },
      "output": {
        "$ref": "#/schemas/Booking"
      }
    }
  ],
  "schemas": {
    "Enterprise": {
      "type": "object",
      "properties": {
        "id": {
          "type": "string"
        },
        "profile": {
          "type": "object",
          "properties": {
            "name": {
              "type": "string"
            }
          }
        },
        "companies": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              }
            }
          }
        }
      }
    },
    "EventType": {
      "type": "object",
      "properties": {
        "id": {
          "type": "string"
        },
        "title": {
          "type": "string"
        },
        "minutes": {
          "type": "integer"
        },
        "payments": {
          "type": "object",
          "properties": {
            "total_payment": {
              "type": "number"
            },
            "pre_payment_required": {
              "type": "boolean"
            },
            "pre_payment_amount": {
              "type": "number"
            }
          }
        }
      }
    },
    "TimeSlotDay": {
      "type": "object",
      "properties": {
        "date": {
          "type": "string",
          "format": "date"
        },
        "slots": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "time": {
                "type": "string"
              },
              "employeeIds": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "Booking": {
      "type": "object",
      "properties": {
        "id": {
          "type": "string"
        },
        "starts_at": {
          "type": "string",
          "format": "date-time"
        },
        "ends_at": {
          "type": "string",
          "format": "date-time"
        },
        "confirmed": {
          "type": "boolean"
        },
        "status": {
          "type": "string"
        },
        "event_types": {
          "type": "array",
          "items": {
            "type": "object"
          }
        },
        "company": {
          "type": "object"
        },
        "employee": {
          "type": "object"
        },
        "payments": {
          "type": "object"
        }
      }
    }
  },
  "origins": [
    "https://noona.is",
    "https://api.noona.is"
  ]
}
