{
  "openapi": "3.1.0",
  "info": {
    "title": "Inflo Third-Party API",
    "version": "1.0.0",
    "description": "Privacy-first API for third-party apps to search Inflo users and send\nnotifications. Mirrors in-app visibility — never grants more.\nEmail addresses are never returned by these endpoints.\n"
  },
  "servers": [
    {
      "url": "https://infloapp.com",
      "description": "Production"
    }
  ],
  "externalDocs": {
    "description": "Agent-friendly quick reference, full RFC 7591 Dynamic Client Registration\nwalkthrough, PAT scopes, and copy-pasteable curl. Start here if you're\nwiring an integration or coding agent.\n",
    "url": "https://api.infloapp.com/agents.md"
  },
  "x-client-registration": {
    "endpoint": "https://api.infloapp.com/oidc/register",
    "alternative_endpoint": "https://api.infloapp.com/api/clp/register-app",
    "spec": "RFC 7591",
    "auth": "Bearer infpat_<personal_access_token>",
    "required_scope": "apps:create",
    "docs": "https://api.infloapp.com/agents.md"
  },
  "paths": {
    "/oidc/register": {
      "post": {
        "summary": "Register a new OIDC client (RFC 7591 Dynamic Client Registration)",
        "description": "Create a new OAuth/OIDC application on the caller's behalf. Implements\n[RFC 7591](https://www.rfc-editor.org/rfc/rfc7591) — point a standards-aware\nOIDC library (e.g. `openid-client`) at this endpoint and it will register\na client without any dashboard click-through.\n\n**This endpoint lives on the API host:** `https://api.infloapp.com/oidc/register`\n(also mirrored at `/api/oidc/register`).\n\n**Auth.** RFC 7591 §3 allows the server to gate registration with an\n`initial_access_token`. We use a Personal Access Token (PAT) with the\n`apps:create` scope. Mint one at\n`https://api.infloapp.com/app/developer/myapps` and pass it as\n`Authorization: Bearer infpat_<token>`.\n\n**Manage afterwards.** The response includes a `registration_access_token`\nand `registration_client_uri` for RFC 7592 GET/PUT/DELETE on the\nregistration itself.\n\n**Proprietary alternative.** `POST /api/clp/register-app` accepts the same\nintent with Inflo-shaped request/response fields (org binding, alias\nmanagement, etc.). Both endpoints create the same kind of app — pick\nwhichever your stack prefers.\n\nFull reference, examples, error mapping, and 24-hour idempotency support\nare documented at `https://api.infloapp.com/agents.md`.\n",
        "tags": [
          "Auth"
        ],
        "externalDocs": {
          "description": "RFC 7591 reference, curl examples, RFC 7592 management endpoints",
          "url": "https://api.infloapp.com/agents.md"
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "client_name"
                ],
                "properties": {
                  "client_name": {
                    "type": "string",
                    "minLength": 2,
                    "maxLength": 100
                  },
                  "redirect_uris": {
                    "type": "array",
                    "description": "Each entry is either an exact URI or a wildcard pattern.\nWildcards (`*`) are only allowed as the **entire\nleftmost DNS label** of the host, e.g.\n`https://*.studio.dripper.live/api/auth/callback`.\nRules:\n  - Only one `*` per URI, and it must be the leftmost\n    label (not a substring, not a path/query/port).\n  - At least two labels must follow the `*` — patterns\n    like `https://*.com/cb` or `https://*.co.uk/cb`\n    that cover a public suffix are rejected (uses the\n    Public Suffix List).\n  - Wildcards require `https://`. `http://` wildcards\n    are rejected (tokens must not cross plaintext\n    networks).\n  - The matched label must be exactly one DNS label —\n    `https://*.foo.com/cb` matches `https://a.foo.com/cb`\n    but not `https://a.b.foo.com/cb` and not the apex\n    `https://foo.com/cb`.\n  - Scheme, port, and path are matched exactly.\n",
                    "items": {
                      "type": "string",
                      "format": "uri"
                    },
                    "example": [
                      "https://app.example.com/oauth/callback",
                      "https://*.studio.dripper.live/api/auth/callback"
                    ]
                  },
                  "grant_types": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "example": [
                      "authorization_code",
                      "refresh_token"
                    ]
                  },
                  "response_types": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "example": [
                      "code"
                    ]
                  },
                  "token_endpoint_auth_method": {
                    "type": "string",
                    "example": "client_secret_basic"
                  },
                  "scope": {
                    "type": "string",
                    "example": "openid profile email"
                  },
                  "application_type": {
                    "type": "string",
                    "enum": [
                      "web",
                      "native"
                    ]
                  },
                  "inflo_org_id": {
                    "oneOf": [
                      {
                        "type": "string"
                      },
                      {
                        "type": "integer"
                      }
                    ],
                    "description": "Optional Inflo org/entity id to own the new app."
                  },
                  "inflo_personal": {
                    "type": "boolean",
                    "description": "Create under the caller's personal entity."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Client registered (RFC 7591 §3.2.1 response shape)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "client_id": {
                      "type": "string"
                    },
                    "client_secret": {
                      "type": "string",
                      "description": "Shown once."
                    },
                    "client_id_issued_at": {
                      "type": "integer"
                    },
                    "client_secret_expires_at": {
                      "type": "integer",
                      "example": 0
                    },
                    "registration_access_token": {
                      "type": "string",
                      "description": "Bearer token for RFC 7592 GET/PUT/DELETE."
                    },
                    "registration_client_uri": {
                      "type": "string",
                      "format": "uri"
                    },
                    "client_name": {
                      "type": "string"
                    },
                    "redirect_uris": {
                      "type": "array",
                      "items": {
                        "type": "string",
                        "format": "uri"
                      }
                    },
                    "grant_types": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "response_types": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "token_endpoint_auth_method": {
                      "type": "string"
                    },
                    "scope": {
                      "type": "string"
                    },
                    "application_type": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "invalid_client_metadata"
          },
          "401": {
            "description": "invalid_token (missing/invalid PAT)"
          },
          "403": {
            "description": "access_denied (PAT lacks apps:create)"
          }
        }
      }
    },
    "/oidc/register/{client_id}": {
      "parameters": [
        {
          "in": "path",
          "name": "client_id",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "summary": "Read a registered client (RFC 7592)",
        "tags": [
          "Auth"
        ],
        "description": "Requires `Authorization: Bearer <registration_access_token>` — the token\nreturned at registration time, NOT your PAT. See `/agents.md`.\n",
        "responses": {
          "200": {
            "description": "Current client metadata"
          },
          "401": {
            "description": "invalid_token"
          },
          "404": {
            "description": "invalid_client"
          }
        }
      },
      "put": {
        "summary": "Update a registered client (RFC 7592)",
        "tags": [
          "Auth"
        ],
        "description": "Updates `redirect_uris` and/or `client_name`. See `/agents.md`.",
        "responses": {
          "200": {
            "description": "Updated client metadata"
          },
          "400": {
            "description": "invalid_client_metadata"
          },
          "401": {
            "description": "invalid_token"
          }
        }
      },
      "delete": {
        "summary": "Deregister a client (RFC 7592)",
        "tags": [
          "Auth"
        ],
        "responses": {
          "204": {
            "description": "Deregistered"
          },
          "401": {
            "description": "invalid_token"
          }
        }
      }
    },
    "/oauth/token": {
      "post": {
        "summary": "Exchange client credentials for an access token",
        "description": "Accepts both `client_secret_basic` (RFC 6749 §2.3.1, preferred) and\n`client_secret_post`. With Basic auth, the credentials in the\n`Authorization: Basic base64(client_id:client_secret)` header are\nsufficient — `client_id`/`client_secret` MUST NOT also be required\nin the body. Standard OAuth libraries (e.g. Node `openid-client`)\nwill work out of the box.\n",
        "tags": [
          "Auth"
        ],
        "security": [
          {
            "basicAuth": []
          },
          {}
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "required": [
                  "grant_type"
                ],
                "properties": {
                  "grant_type": {
                    "type": "string",
                    "enum": [
                      "client_credentials"
                    ]
                  },
                  "client_id": {
                    "type": "string",
                    "description": "Required only when not using `client_secret_basic`."
                  },
                  "client_secret": {
                    "type": "string",
                    "description": "Required only when not using `client_secret_basic`."
                  },
                  "scope": {
                    "type": "string",
                    "description": "Space-delimited list (e.g. `users:search notifications:write`)"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Token issued",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "access_token": {
                      "type": "string"
                    },
                    "token_type": {
                      "type": "string",
                      "enum": [
                        "Bearer"
                      ]
                    },
                    "expires_in": {
                      "type": "integer",
                      "example": 3600
                    },
                    "scope": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/users/search": {
      "get": {
        "summary": "Search discoverable Inflo users by name or username",
        "description": "Only returns users who have **Allow third-party discovery** enabled\nin their privacy settings AND have not hidden themselves from\nin-app search.\n",
        "tags": [
          "Users"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "in": "query",
            "name": "q",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 2
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Matching users",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "results": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/PublicUser"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing/invalid token"
          },
          "403": {
            "description": "Token lacks `users:search` scope"
          }
        }
      }
    },
    "/api/v1/users/{sub}": {
      "get": {
        "summary": "Get a discoverable user's public profile by sub",
        "tags": [
          "Users"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "sub",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Public profile",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicUser"
                }
              }
            }
          },
          "404": {
            "description": "Not found or not discoverable"
          }
        }
      }
    },
    "/api/v1/organizations/{id}/members": {
      "get": {
        "summary": "List members of an organization (admin-only)",
        "description": "Returns a paginated list of active members for the given org.\n\n**Authorization model.** This endpoint is only callable by an OAuth\nclient that has been bound to the same org and granted an `admin` or\n`owner` role at registration time. The issued bearer token carries\n`org_id` and `org_role` claims; if `org_id` doesn't match `{id}` or\n`org_role` is not `admin`/`owner`, the route returns **403**.\n\nMembers who have **Allow third-party discovery** turned off are\nalways excluded from the response, even though the caller is an\nadmin — a per-user opt-out always wins over admin enumeration.\n\nUse this to auto-populate a tenant-collaboration app (e.g. a shared\nSalus Studio workspace) with the org's existing team without\nwaiting for each member to sign in individually.\n",
        "tags": [
          "Organizations"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "description": "Org id in `org_<n>` form.",
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 25
            }
          },
          {
            "in": "query",
            "name": "cursor",
            "required": false,
            "description": "Opaque pagination cursor returned by a previous call.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated list of org members.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/OrgMember"
                      }
                    },
                    "nextCursor": {
                      "type": "string",
                      "nullable": true
                    },
                    "totalCount": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid org id format or cursor"
          },
          "401": {
            "description": "Missing/invalid token"
          },
          "403": {
            "description": "Token lacks `org:members:read`, isn't bound to this org, or the\nclient's `org_role` is not `admin`/`owner`.\n"
          },
          "404": {
            "description": "Organization not found"
          }
        }
      }
    },
    "/api/v1/notifications": {
      "post": {
        "summary": "Send a notification to an Inflo user",
        "description": "Delivers an in-app notification (and optionally an email, if the\nrecipient has both toggles enabled). Returns 202 with no body when\nthe recipient has not opted in — this avoids leaking whether the\nuser exists vs has it disabled.\n",
        "tags": [
          "Notifications"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "sub",
                  "title",
                  "body"
                ],
                "properties": {
                  "sub": {
                    "type": "string",
                    "description": "Recipient's Inflo sub (id)"
                  },
                  "title": {
                    "type": "string",
                    "maxLength": 200
                  },
                  "body": {
                    "type": "string",
                    "maxLength": 1000
                  },
                  "deepLink": {
                    "type": "string",
                    "format": "uri"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Delivered",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "integer"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "delivered"
                      ]
                    },
                    "deliveredInApp": {
                      "type": "boolean"
                    },
                    "deliveredEmail": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "202": {
            "description": "Recipient has not opted in. Returned with no body to avoid leaking\nwhether the user exists vs has notifications disabled.\n"
          },
          "400": {
            "description": "Invalid request"
          }
        }
      }
    },
    "/api/clp/webhooks": {
      "get": {
        "summary": "List webhooks for the authenticated CLP tenant",
        "tags": [
          "Webhooks"
        ],
        "security": [
          {
            "ClpTenantKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "List of webhooks",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "webhooks": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Webhook"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "summary": "Register a new webhook endpoint",
        "tags": [
          "Webhooks"
        ],
        "security": [
          {
            "ClpTenantKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "url",
                  "events"
                ],
                "properties": {
                  "url": {
                    "type": "string",
                    "format": "uri"
                  },
                  "events": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/WebhookEvent"
                    }
                  },
                  "description": {
                    "type": "string"
                  },
                  "maxRetries": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 20
                  },
                  "retryBackoffSeconds": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 3600
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Webhook created; the signing secret is returned once.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "webhookId": {
                      "type": "integer"
                    },
                    "secret": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/clp/webhooks/{id}": {
      "parameters": [
        {
          "in": "path",
          "name": "id",
          "required": true,
          "schema": {
            "type": "integer"
          }
        }
      ],
      "patch": {
        "summary": "Update a webhook (URL, events, active state, retry config)",
        "tags": [
          "Webhooks"
        ],
        "security": [
          {
            "ClpTenantKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Updated"
          }
        }
      },
      "delete": {
        "summary": "Delete a webhook",
        "tags": [
          "Webhooks"
        ],
        "security": [
          {
            "ClpTenantKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted"
          }
        }
      }
    },
    "/api/clp/webhooks/{id}/deliveries": {
      "get": {
        "summary": "List recent deliveries for a webhook",
        "tags": [
          "Webhooks"
        ],
        "security": [
          {
            "ClpTenantKey": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "in": "query",
            "name": "limit",
            "schema": {
              "type": "integer",
              "default": 50,
              "maximum": 200
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Recent deliveries",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "deliveries": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/WebhookDelivery"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/clp/webhook-deliveries/{id}": {
      "get": {
        "summary": "Get a single delivery (with full payload)",
        "tags": [
          "Webhooks"
        ],
        "security": [
          {
            "ClpTenantKey": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Delivery"
          },
          "404": {
            "description": "Not found"
          }
        }
      }
    },
    "/api/clp/webhook-deliveries/{id}/replay": {
      "post": {
        "summary": "Replay a delivery — enqueues a fresh attempt with the original payload",
        "tags": [
          "Webhooks"
        ],
        "security": [
          {
            "ClpTenantKey": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "202": {
            "description": "Replay queued",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "deliveryId": {
                      "type": "integer"
                    },
                    "status": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not found"
          },
          "409": {
            "description": "Webhook is disabled"
          }
        }
      }
    },
    "/api/smart-views/{id}/access-tokens": {
      "get": {
        "summary": "List external API tokens for a SmartView",
        "tags": [
          "SmartView Access Tokens"
        ],
        "security": [
          {
            "bearerPat": [
              "smartviews:manage"
            ]
          },
          {
            "sessionCookie": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Tokens list"
          },
          "401": {
            "description": "Not authenticated"
          },
          "403": {
            "description": "Not the SmartView owner",
            "or PAT missing scope": null
          }
        }
      },
      "post": {
        "summary": "Issue a new external API token (shown once)",
        "tags": [
          "SmartView Access Tokens"
        ],
        "security": [
          {
            "bearerPat": [
              "smartviews:manage"
            ]
          },
          {
            "sessionCookie": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 2,
                    "maxLength": 120
                  },
                  "expiresInDays": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 365
                  },
                  "ipAllowlist": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "IPs or CIDRs allowed to call with this token."
                  },
                  "rateLimitTier": {
                    "type": "string",
                    "enum": [
                      "low",
                      "default",
                      "high"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Token issued — `token` field is shown once.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "token": {
                      "type": "string",
                      "description": "Plaintext `infsv_…` value",
                      "shown once.": null
                    },
                    "details": {
                      "type": "object"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation failed"
          },
          "401": {
            "description": "Not authenticated"
          },
          "403": {
            "description": "Not the SmartView owner",
            "or PAT missing scope": null
          }
        }
      }
    },
    "/api/smart-views/{id}/access-tokens/{tokenId}": {
      "delete": {
        "summary": "Revoke an external API token",
        "tags": [
          "SmartView Access Tokens"
        ],
        "security": [
          {
            "bearerPat": [
              "smartviews:manage"
            ]
          },
          {
            "sessionCookie": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "in": "path",
            "name": "tokenId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Revoked"
          },
          "404": {
            "description": "Token not found"
          }
        }
      }
    },
    "/api/smart-views/{id}/access-tokens/{tokenId}/activity": {
      "get": {
        "summary": "Per-token activity feed (calls + management events)",
        "tags": [
          "SmartView Access Tokens"
        ],
        "security": [
          {
            "bearerPat": [
              "smartviews:manage"
            ]
          },
          {
            "sessionCookie": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "in": "path",
            "name": "tokenId",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "limit",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Activity rows"
          },
          "404": {
            "description": "Token not found"
          }
        }
      }
    },
    "/api/v1/smartviews/{slug}": {
      "get": {
        "summary": "Get SmartView metadata",
        "description": "Returns the SmartView's identity, owner profile, backing object type,\nand field schema (label, type, visibility, masking) — but no row\ndata. Use this once at integration time to render UI; call `/rows`\nfor the actual data.\n",
        "tags": [
          "SmartView External API"
        ],
        "security": [
          {
            "bearerSmartView": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "slug",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "SmartView slug or numeric id."
          }
        ],
        "responses": {
          "200": {
            "description": "SmartView metadata",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "smartView": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "integer"
                        },
                        "name": {
                          "type": "string"
                        },
                        "slug": {
                          "type": "string"
                        },
                        "description": {
                          "type": "string",
                          "nullable": true
                        }
                      }
                    },
                    "owner": {
                      "type": "object",
                      "nullable": true
                    },
                    "smartObject": {
                      "type": "object"
                    },
                    "fieldSchema": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "label": {
                            "type": "string",
                            "nullable": true
                          },
                          "type": {
                            "type": "string",
                            "nullable": true
                          },
                          "visible": {
                            "type": "boolean"
                          },
                          "masked": {
                            "type": "boolean"
                          }
                        }
                      }
                    },
                    "requestId": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Token missing",
            "malformed": null,
            "revoked": null,
            "or expired": null
          },
          "403": {
            "description": "Token not authorized for this resource or IP blocked"
          },
          "404": {
            "description": "SmartView not found"
          },
          "429": {
            "description": "Per-token rate limit exceeded"
          }
        }
      }
    },
    "/api/v1/smartviews/{slug}/rows": {
      "get": {
        "summary": "List rows from a SmartView",
        "description": "Returns rows visible through the SmartView, with field visibility\nand masking applied server-side. Single-row SmartObject types (card,\nbio, page) always return one row; multi-row types (e.g. listings)\nsupport cursor pagination.\n",
        "tags": [
          "SmartView External API"
        ],
        "security": [
          {
            "bearerSmartView": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "slug",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "limit",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            }
          },
          {
            "in": "query",
            "name": "cursor",
            "schema": {
              "type": "string"
            },
            "description": "Opaque cursor returned as `nextCursor` from a previous call."
          }
        ],
        "responses": {
          "200": {
            "description": "Rows",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "rows": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "fields": {
                            "type": "array",
                            "items": {
                              "type": "object",
                              "properties": {
                                "id": {
                                  "type": "string"
                                },
                                "label": {
                                  "type": "string",
                                  "nullable": true
                                },
                                "type": {
                                  "type": "string",
                                  "nullable": true
                                },
                                "value": {
                                  "type": "string",
                                  "nullable": true
                                },
                                "masked": {
                                  "type": "boolean"
                                }
                              }
                            }
                          }
                        }
                      }
                    },
                    "nextCursor": {
                      "type": "string",
                      "nullable": true
                    },
                    "requestId": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Token missing",
            "malformed": null,
            "revoked": null,
            "or expired": null
          },
          "403": {
            "description": "Token not authorized for this resource or IP blocked"
          },
          "404": {
            "description": "SmartView not found"
          },
          "429": {
            "description": "Per-token rate limit exceeded"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT",
        "description": "OAuth2 client-credentials JWT obtained from `POST /oauth/token`.\nUsed for app-to-Inflo calls about other users (search, send\nnotifications, etc.). Tokens expire in 1 hour.\n"
      },
      "basicAuth": {
        "type": "http",
        "scheme": "basic",
        "description": "RFC 6749 §2.3.1 `client_secret_basic` — `Authorization: Basic\nbase64(client_id:client_secret)`. Used to authenticate to the\n`POST /oauth/token` endpoint. Preferred over passing the\ncredentials in the request body.\n"
      },
      "sessionCookie": {
        "type": "apiKey",
        "in": "cookie",
        "name": "connect.sid",
        "description": "Inflo web session cookie (set by signing in at infloapp.com)."
      },
      "bearerSmartView": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "infsv_token",
        "description": "SmartView External API access token (Task #194). Owner-issued from\nthe SmartView management UI; each token is bound to exactly one\nSmartView and inherits its field visibility / masking rules. Tokens\nare prefixed `infsv_`, shown once, and revocable at any time. Rate\nlimits per token are returned on every response via the\n`X-Inflo-RateLimit-Limit` / `-Remaining` / `-Reset` headers.\n"
      },
      "bearerPat": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "infpat_token",
        "description": "Personal Access Token (PAT) for programmatic management of resources\nyou own, e.g. updating an SSO app's redirect URIs. Tokens are prefixed\n`infpat_`, generated via `POST /api/personal-access-tokens` from a\nbrowser session, and shown only once. Carries one or more of the\nscopes `apps:read`, `apps:create`, `apps:manage`.\n\nAccepted on `POST /api/clp/register-app` and every `/api/clp/my-apps/*`\nroute (list, get/regenerate credentials, update/delete app,\nauth-settings, IP rules, login-attempt limits, risk assessment,\nbranding incl. logo upload). Read endpoints require `apps:read`;\nwrite endpoints and the credentials endpoint require `apps:manage`;\n`POST /api/clp/register-app` requires `apps:create`.\n\nAuth failures return a structured body with `error`, `code`, and\n`hint`. Codes include `missing_authorization`, `invalid_auth_scheme`,\n`empty_token`, `invalid_token_prefix`, `unknown_token`,\n`token_revoked`, `token_expired`, `missing_scope`, and\n`pat_entity_mismatch` (when the PAT is bound to specific entity\n(org) IDs and the targeted app belongs to a different entity).\n\nPATs are NOT accepted on `/api/auth/me`, `/api/users/me`, or\n`/api/personal-access-tokens` — those are session-only by design.\nVerify a PAT with `GET /api/personal-access-tokens/whoami`.\n"
      }
    },
    "schemas": {
      "Webhook": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "events": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WebhookEvent"
            }
          },
          "isActive": {
            "type": "boolean"
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "maxRetries": {
            "type": "integer"
          },
          "retryBackoffSeconds": {
            "type": "integer"
          },
          "lastSuccessAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "lastFailureAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "consecutiveFailures": {
            "type": "integer"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "WebhookEvent": {
        "type": "string",
        "enum": [
          "user.created",
          "user.joined_org",
          "user.left_org",
          "connection.created",
          "smartview.shared",
          "share.created",
          "share.accepted",
          "share.declined",
          "share.revoked",
          "share.expired",
          "share.accessed",
          "resource.updated"
        ]
      },
      "WebhookDelivery": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "eventType": {
            "$ref": "#/components/schemas/WebhookEvent"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "delivering",
              "retrying",
              "delivered",
              "failed",
              "skipped"
            ]
          },
          "attempts": {
            "type": "integer"
          },
          "responseStatusCode": {
            "type": "integer",
            "nullable": true
          },
          "responseTimeMs": {
            "type": "integer",
            "nullable": true
          },
          "errorMessage": {
            "type": "string",
            "nullable": true
          },
          "nextAttemptAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "lastAttemptAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "OrgMember": {
        "type": "object",
        "properties": {
          "sub": {
            "type": "string",
            "description": "Recipient's Inflo sub (id)"
          },
          "name": {
            "type": "string"
          },
          "picture": {
            "type": "string",
            "nullable": true
          },
          "role": {
            "type": "string",
            "description": "Membership role inside the org.",
            "enum": [
              "owner",
              "admin",
              "editor",
              "member",
              "viewer"
            ]
          },
          "verifiedEmail": {
            "type": "boolean",
            "description": "Whether the member has at least one verified email identity."
          },
          "verifiedPhone": {
            "type": "boolean",
            "description": "Whether the member has at least one verified phone identity."
          },
          "verificationLevel": {
            "type": "string",
            "enum": [
              "none",
              "basic",
              "id_checked"
            ],
            "description": "Highest identity-verification level on the user. `id_checked`\nmeans government ID was verified; `basic` means at least one\nchannel (email/phone) is verified; `none` means neither.\n"
          }
        }
      },
      "PublicUser": {
        "type": "object",
        "properties": {
          "sub": {
            "type": "string"
          },
          "displayName": {
            "type": "string"
          },
          "userName": {
            "type": "string",
            "nullable": true
          },
          "picture": {
            "type": "string",
            "nullable": true
          },
          "coverPhoto": {
            "type": "string",
            "nullable": true
          },
          "profession": {
            "type": "string",
            "nullable": true
          },
          "slug": {
            "type": "string",
            "nullable": true
          },
          "moodMessage": {
            "type": "string",
            "nullable": true
          },
          "isAvailable": {
            "type": "boolean",
            "nullable": true
          },
          "verifiedEmail": {
            "type": "boolean",
            "description": "`true` when this user has at least one verified email identity on\nfile. The actual email value is never returned by this endpoint.\n"
          },
          "verifiedPhone": {
            "type": "boolean",
            "description": "`true` when this user has at least one verified phone identity on\nfile. The actual phone value is never returned by this endpoint.\n"
          },
          "verificationLevel": {
            "type": "string",
            "enum": [
              "none",
              "basic",
              "id_checked"
            ],
            "description": "Highest verification level reached:\n  * `none` — no verified channels.\n  * `basic` — at least one verified email or phone identity.\n  * `id_checked` — user has completed government-ID / KYC-style\n    verification.\n"
          }
        }
      }
    }
  }
}