{
  "openapi": "3.1.0",
  "info": {
    "title": "Sounds API",
    "version": "1.0.0",
    "description": "REST API for the Sounds mobile platform. Authenticate with a Supabase JWT (end users) or an admin-issued `sk_live_` API key (server-to-server). All endpoints return JSON.",
    "contact": {
      "name": "Sounds API",
      "url": "https://app.aparmita.com/docs/api"
    }
  },
  "servers": [
    {
      "url": "https://app.aparmita.com/api/v1",
      "description": "Production"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "Auth",
      "description": "Sign up, sign in, refresh, and sign out for mobile apps."
    },
    {
      "name": "Sounds",
      "description": "Browse and stream sounds."
    },
    {
      "name": "Favorites",
      "description": "Per-user favorites."
    },
    {
      "name": "Account",
      "description": "Current user."
    },
    {
      "name": "Home",
      "description": "Homepage sections."
    }
  ],
  "paths": {
    "/auth/signup": {
      "post": {
        "tags": [
          "Auth"
        ],
        "summary": "Create an account",
        "description": "Email + password sign-up. Returns tokens when email confirmation is disabled; otherwise returns `needs_email_confirmation: true`.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email",
                  "password"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email"
                  },
                  "password": {
                    "type": "string",
                    "minLength": 6
                  },
                  "full_name": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Session tokens.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "access_token": {
                          "type": "string"
                        },
                        "refresh_token": {
                          "type": "string"
                        },
                        "token_type": {
                          "type": "string"
                        },
                        "expires_at": {
                          "type": "integer"
                        },
                        "expires_in": {
                          "type": "integer"
                        },
                        "needs_email_confirmation": {
                          "type": "boolean"
                        },
                        "user": {
                          "type": "object",
                          "properties": {
                            "id": {
                              "type": "string",
                              "format": "uuid"
                            },
                            "email": {
                              "type": [
                                "string",
                                "null"
                              ]
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/auth/signin": {
      "post": {
        "tags": [
          "Auth"
        ],
        "summary": "Sign in with email + password",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email",
                  "password"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email"
                  },
                  "password": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Session tokens.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "access_token": {
                          "type": "string"
                        },
                        "refresh_token": {
                          "type": "string"
                        },
                        "token_type": {
                          "type": "string"
                        },
                        "expires_at": {
                          "type": "integer"
                        },
                        "expires_in": {
                          "type": "integer"
                        },
                        "needs_email_confirmation": {
                          "type": "boolean"
                        },
                        "user": {
                          "type": "object",
                          "properties": {
                            "id": {
                              "type": "string",
                              "format": "uuid"
                            },
                            "email": {
                              "type": [
                                "string",
                                "null"
                              ]
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/auth/refresh": {
      "post": {
        "tags": [
          "Auth"
        ],
        "summary": "Refresh access token",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "refresh_token"
                ],
                "properties": {
                  "refresh_token": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Session tokens.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "access_token": {
                          "type": "string"
                        },
                        "refresh_token": {
                          "type": "string"
                        },
                        "token_type": {
                          "type": "string"
                        },
                        "expires_at": {
                          "type": "integer"
                        },
                        "expires_in": {
                          "type": "integer"
                        },
                        "needs_email_confirmation": {
                          "type": "boolean"
                        },
                        "user": {
                          "type": "object",
                          "properties": {
                            "id": {
                              "type": "string",
                              "format": "uuid"
                            },
                            "email": {
                              "type": [
                                "string",
                                "null"
                              ]
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid refresh token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/auth/signout": {
      "post": {
        "tags": [
          "Auth"
        ],
        "summary": "Sign out (revoke refresh token)",
        "description": "Requires a Bearer access token. Revokes the associated refresh token server-side.",
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "ok": {
                          "type": "boolean"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/auth/oauth/google": {
      "post": {
        "tags": [
          "Auth"
        ],
        "summary": "Exchange a Google ID token for a session",
        "description": "Pass the `id_token` from the native Google Sign-In SDK (iOS/Android) to exchange it for app tokens.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id_token"
                ],
                "properties": {
                  "id_token": {
                    "type": "string"
                  },
                  "nonce": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Session tokens.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "access_token": {
                          "type": "string"
                        },
                        "refresh_token": {
                          "type": "string"
                        },
                        "token_type": {
                          "type": "string"
                        },
                        "expires_at": {
                          "type": "integer"
                        },
                        "expires_in": {
                          "type": "integer"
                        },
                        "needs_email_confirmation": {
                          "type": "boolean"
                        },
                        "user": {
                          "type": "object",
                          "properties": {
                            "id": {
                              "type": "string",
                              "format": "uuid"
                            },
                            "email": {
                              "type": [
                                "string",
                                "null"
                              ]
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Google sign-in failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/sounds": {
      "get": {
        "tags": [
          "Sounds"
        ],
        "summary": "List sounds",
        "description": "Returns active sounds, ordered by sort order then most recent. Supports cursor pagination.",
        "parameters": [
          {
            "name": "category",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "featured",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "search",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "ISO8601 timestamp from `next_cursor`.",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated sounds.",
            "headers": {
              "X-RateLimit-Limit": {
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Sound"
                      }
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "date-time"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/sounds/{id}": {
      "get": {
        "tags": [
          "Sounds"
        ],
        "summary": "Get sound with signed audio URL",
        "description": "Returns the full sound record with a short-lived (5 min) signed audio URL. Re-request immediately before playback; do not cache.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Sound ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Sound detail.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/SoundDetail"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/sounds/{id}/play": {
      "post": {
        "tags": [
          "Sounds"
        ],
        "summary": "Track a play",
        "description": "Atomically increments the sound's global play counter.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Sound ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Updated play count.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string",
                          "format": "uuid"
                        },
                        "play_count": {
                          "type": "integer"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/favorites": {
      "get": {
        "tags": [
          "Favorites"
        ],
        "summary": "List favorites",
        "responses": {
          "200": {
            "description": "User favorites.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Favorite"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Favorites"
        ],
        "summary": "Add a favorite (idempotent)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "sound_id"
                ],
                "properties": {
                  "sound_id": {
                    "type": "string",
                    "format": "uuid"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "ok": {
                          "type": "boolean"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/favorites/{soundId}": {
      "delete": {
        "tags": [
          "Favorites"
        ],
        "summary": "Remove a favorite",
        "parameters": [
          {
            "name": "soundId",
            "in": "path",
            "required": true,
            "description": "Sound to unfavorite",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "ok": {
                          "type": "boolean"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/me": {
      "get": {
        "tags": [
          "Account"
        ],
        "summary": "Current user",
        "description": "Returns the caller's profile. Useful for validating a token after sign-in.",
        "responses": {
          "200": {
            "description": "Profile.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Profile"
                    },
                    "auth_via": {
                      "type": "string",
                      "enum": [
                        "jwt",
                        "api_key"
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/me/customer": {
      "get": {
        "tags": [
          "Account"
        ],
        "summary": "Current customer",
        "description": "Returns the caller's customer record (end-user app profile).",
        "responses": {
          "200": {
            "description": "Customer.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Customer"
                    },
                    "auth_via": {
                      "type": "string",
                      "enum": [
                        "jwt",
                        "api_key"
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "patch": {
        "tags": [
          "Account"
        ],
        "summary": "Update current customer",
        "description": "Update the caller's customer record. Only the listed fields can be changed; email & provider are managed by auth.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "full_name": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "avatar_url": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "phone": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "locale": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "country": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "marketing_opt_in": {
                    "type": "boolean"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated customer.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Customer"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/home": {
      "get": {
        "tags": [
          "Home"
        ],
        "summary": "Homepage sections",
        "description": "Returns active homepage sections in display order, each with its resolved list of sounds (manual picks or rule-based).",
        "responses": {
          "200": {
            "description": "Sections with resolved sounds.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "sections": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string",
                            "format": "uuid"
                          },
                          "slug": {
                            "type": "string"
                          },
                          "title": {
                            "type": "string"
                          },
                          "mode": {
                            "type": "string",
                            "enum": [
                              "manual",
                              "rule"
                            ]
                          },
                          "rule_type": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "enum": [
                              "category",
                              "featured",
                              "newest",
                              null
                            ]
                          },
                          "rule_value": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "sounds": {
                            "type": "array",
                            "items": {
                              "$ref": "#/components/schemas/Sound"
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT or sk_live_*",
        "description": "Pass either a Supabase access token (end users) or an admin-issued API key beginning with `sk_live_`."
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "invalid_request",
                  "unauthorized",
                  "not_found",
                  "rate_limited",
                  "server_error"
                ]
              },
              "message": {
                "type": "string"
              }
            }
          }
        }
      },
      "Sound": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "title": {
            "type": "string"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "bowl_note": {
            "type": [
              "string",
              "null"
            ]
          },
          "frequency_hz": {
            "type": [
              "number",
              "null"
            ]
          },
          "duration_sec": {
            "type": [
              "number",
              "null"
            ]
          },
          "category": {
            "type": [
              "string",
              "null"
            ]
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "nullable": true
          },
          "cover_image_url": {
            "type": [
              "string",
              "null"
            ]
          },
          "is_featured": {
            "type": "boolean"
          },
          "play_count": {
            "type": "integer"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "title",
          "is_featured",
          "play_count",
          "created_at"
        ]
      },
      "SoundDetail": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Sound"
          },
          {
            "type": "object",
            "properties": {
              "audio_url": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Short-lived signed URL (expires in 5 minutes)."
              },
              "audio_url_expires_in": {
                "type": "integer",
                "description": "Seconds until the signed URL expires."
              }
            }
          }
        ]
      },
      "Favorite": {
        "type": "object",
        "properties": {
          "sound_id": {
            "type": "string",
            "format": "uuid"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "sounds": {
            "type": "object",
            "nullable": true,
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid"
              },
              "title": {
                "type": "string"
              },
              "category": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "cover_image_url": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "duration_sec": {
                "type": [
                  "number",
                  "null"
                ]
              }
            }
          }
        }
      },
      "Profile": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "email": {
            "type": [
              "string",
              "null"
            ]
          },
          "full_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "avatar_url": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "Customer": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "email": {
            "type": [
              "string",
              "null"
            ]
          },
          "full_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "avatar_url": {
            "type": [
              "string",
              "null"
            ]
          },
          "phone": {
            "type": [
              "string",
              "null"
            ]
          },
          "provider": {
            "type": [
              "string",
              "null"
            ]
          },
          "locale": {
            "type": [
              "string",
              "null"
            ]
          },
          "country": {
            "type": [
              "string",
              "null"
            ]
          },
          "marketing_opt_in": {
            "type": "boolean"
          },
          "is_active": {
            "type": "boolean"
          },
          "last_seen_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "marketing_opt_in",
          "is_active",
          "created_at",
          "updated_at"
        ]
      }
    }
  }
}