{
  "openapi": "3.1.0",
  "info": {
    "title": "Hybrid ID Guest Profiles",
    "version": "1.1.0",
    "description": "App-scoped guest registration and user-approved account linking. See /integrations/guest-profiles.md for enrollment and security requirements."
  },
  "servers": [
    {
      "url": "https://hybrid-id.com"
    }
  ],
  "components": {
    "securitySchemes": {
      "guestCredential": {
        "type": "http",
        "scheme": "bearer",
        "description": "App-scoped hid_guest_ token. An OIDC client secret is not accepted."
      }
    },
    "schemas": {
      "GuestError": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "success",
          "error"
        ],
        "properties": {
          "success": {
            "const": false
          },
          "error": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "code",
              "retryable"
            ],
            "properties": {
              "code": {
                "type": "string"
              },
              "retryable": {
                "type": "boolean",
                "description": "False for 4xx except 429; true for 429/5xx. A disabled feature still requires configuration repair even when the transport flag is true."
              }
            }
          }
        }
      }
    }
  },
  "paths": {
    "/api/developer/v1/guests": {
      "post": {
        "operationId": "guestProfileOperation",
        "security": [
          {
            "guestCredential": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "oneOf": [
                  {
                    "type": "object",
                    "additionalProperties": false,
                    "properties": {
                      "action": {
                        "type": "string",
                        "const": "create"
                      },
                      "external_guest_id": {
                        "type": "string",
                        "pattern": "^[a-f0-9]{32}$"
                      },
                      "claim_secret_sha256": {
                        "type": "string",
                        "pattern": "^[a-f0-9]{64}$",
                        "description": "SHA-256 of the 43-character base64url-encoded claim secret string, not decoded bytes. Generate a random 32-byte secret."
                      }
                    },
                    "required": [
                      "action",
                      "external_guest_id",
                      "claim_secret_sha256"
                    ],
                    "title": "Create",
                    "description": "Same app, external ID and claim digest returns the same record. Different proof returns GUEST_REGISTRATION_CONFLICT. No extra idempotency header."
                  },
                  {
                    "type": "object",
                    "additionalProperties": false,
                    "properties": {
                      "action": {
                        "type": "string",
                        "const": "read"
                      },
                      "guest_profile_id": {
                        "type": "string",
                        "pattern": "^[a-f0-9]{32}$"
                      }
                    },
                    "required": [
                      "action",
                      "guest_profile_id"
                    ],
                    "title": "Read",
                    "description": "Returns only the credential app\u2019s record; no canonical identity, email, secret or linked subject."
                  },
                  {
                    "type": "object",
                    "additionalProperties": false,
                    "properties": {
                      "action": {
                        "type": "string",
                        "const": "link"
                      },
                      "guest_profile_id": {
                        "type": "string",
                        "pattern": "^[a-f0-9]{32}$"
                      },
                      "guest_secret": {
                        "type": "string",
                        "pattern": "^[A-Za-z0-9_-]{43}$",
                        "writeOnly": true
                      },
                      "access_token": {
                        "type": "string",
                        "writeOnly": true,
                        "description": "Current same-app OIDC access token with openid and guest:link; not an ID token or developer credential."
                      }
                    },
                    "required": [
                      "action",
                      "guest_profile_id",
                      "guest_secret",
                      "access_token"
                    ],
                    "title": "Link",
                    "description": "Idempotently links to the user authenticated in this app; a different identity cannot overwrite an existing link. Validate issuer/sub against the ID token."
                  },
                  {
                    "type": "object",
                    "additionalProperties": false,
                    "properties": {
                      "action": {
                        "type": "string",
                        "const": "usage"
                      }
                    },
                    "required": [
                      "action"
                    ],
                    "title": "Usage",
                    "description": "Pooled developer-wide counts, not app-only counts or unique people."
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success envelope: success=true, data includes guest profile or pooled usage.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "success",
                    "data"
                  ],
                  "properties": {
                    "success": {
                      "const": true
                    },
                    "data": {
                      "oneOf": [
                        {
                          "type": "object",
                          "additionalProperties": false,
                          "properties": {
                            "guest_profile_id": {
                              "type": "string",
                              "pattern": "^[a-f0-9]{32}$"
                            },
                            "state": {
                              "enum": [
                                "guest",
                                "linked"
                              ]
                            },
                            "created_at": {
                              "type": "integer",
                              "minimum": 0
                            },
                            "linked_at": {
                              "type": [
                                "integer",
                                "null"
                              ]
                            },
                            "created": {
                              "type": "boolean"
                            },
                            "proof_status": {
                              "const": "not_published"
                            },
                            "sub": {
                              "type": "string"
                            },
                            "issuer": {
                              "const": "https://auth.hybrid-id.com"
                            }
                          },
                          "required": [
                            "guest_profile_id",
                            "state",
                            "created_at",
                            "linked_at",
                            "created",
                            "proof_status"
                          ]
                        },
                        {
                          "type": "object",
                          "additionalProperties": false,
                          "properties": {
                            "retained_guest_profiles": {
                              "type": "integer",
                              "minimum": 0,
                              "description": "All preserved guest records, including linked history; not quota usage."
                            },
                            "unclaimed": {
                              "type": "integer",
                              "minimum": 0,
                              "description": "Guest records occupying capacity. Successfully authorized linking changes their state and preserves history."
                            },
                            "linked": {
                              "type": "integer",
                              "minimum": 0
                            },
                            "limit": {
                              "type": [
                                "integer",
                                "null"
                              ],
                              "minimum": 0,
                              "description": "5 times included plan MAU; cap on unclaimed guests pooled across developer apps."
                            },
                            "scope": {
                              "const": "developer_all_apps"
                            },
                            "counts_toward_monthly_users": {
                              "type": "boolean",
                              "const": false,
                              "description": "Guest records do not consume the active-user allowance."
                            },
                            "guest_usage_charges_enabled": {
                              "type": "boolean",
                              "const": false,
                              "description": "Guest access is not billed at present."
                            },
                            "remaining": {
                              "type": [
                                "integer",
                                "null"
                              ],
                              "minimum": 0,
                              "description": "max(0, limit - unclaimed), including after a downgrade."
                            },
                            "quota_basis": {
                              "const": "unclaimed_guest_profiles"
                            },
                            "usage_warning": {
                              "enum": [
                                null,
                                "approaching_limit",
                                "limit_reached"
                              ],
                              "description": "null below 80%; approaching_limit at 80% up to but excluding 100%; limit_reached at or above 100%."
                            },
                            "unlimited": {
                              "type": "boolean",
                              "description": "When true, this account has no guest-capacity ceiling; limit and remaining are null."
                            }
                          },
                          "required": [
                            "retained_guest_profiles",
                            "unclaimed",
                            "linked",
                            "limit",
                            "scope",
                            "counts_toward_monthly_users",
                            "guest_usage_charges_enabled",
                            "remaining",
                            "quota_basis",
                            "usage_warning"
                          ]
                        }
                      ]
                    }
                  }
                },
                "examples": {
                  "usage": {
                    "value": {
                      "success": true,
                      "data": {
                        "retained_guest_profiles": 4500,
                        "unclaimed": 4000,
                        "linked": 500,
                        "limit": 5000,
                        "remaining": 1000,
                        "quota_basis": "unclaimed_guest_profiles",
                        "usage_warning": "approaching_limit",
                        "scope": "developer_all_apps",
                        "counts_toward_monthly_users": false,
                        "guest_usage_charges_enabled": false
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid JSON object, fields or guest proof format.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GuestError"
                },
                "examples": {
                  "typical": {
                    "value": {
                      "success": false,
                      "error": {
                        "code": "INVALID_GUEST_REQUEST",
                        "retryable": false
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid, expired or revoked app-scoped guest credential.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GuestError"
                },
                "examples": {
                  "typical": {
                    "value": {
                      "success": false,
                      "error": {
                        "code": "INVALID_GUEST_CREDENTIAL",
                        "retryable": false
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Guest feature, scope, ownership, guest proof or live linking consent denied.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GuestError"
                },
                "examples": {
                  "typical": {
                    "value": {
                      "success": false,
                      "error": {
                        "code": "GUEST_LINK_CONSENT_REQUIRED",
                        "retryable": false
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Guest not available in this app.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GuestError"
                },
                "examples": {
                  "typical": {
                    "value": {
                      "success": false,
                      "error": {
                        "code": "GUEST_NOT_FOUND",
                        "retryable": false
                      }
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "Capacity reached, registration proof conflict or already linked to a different identity. Quota rejection requires a developer upgrade action, not automatic retries.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GuestError"
                },
                "examples": {
                  "typical": {
                    "value": {
                      "success": false,
                      "error": {
                        "code": "GUEST_PROFILE_QUOTA",
                        "retryable": false
                      }
                    }
                  },
                  "GUEST_REGISTRATION_CONFLICT": {
                    "value": {
                      "success": false,
                      "error": {
                        "code": "GUEST_REGISTRATION_CONFLICT",
                        "retryable": false
                      }
                    }
                  },
                  "GUEST_ALREADY_LINKED": {
                    "value": {
                      "success": false,
                      "error": {
                        "code": "GUEST_ALREADY_LINKED",
                        "retryable": false
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Developer-wide guest rate limit exceeded. Honor Retry-After with bounded backoff.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GuestError"
                },
                "examples": {
                  "typical": {
                    "value": {
                      "success": false,
                      "error": {
                        "code": "GUEST_RATE_LIMIT",
                        "retryable": true
                      }
                    }
                  }
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Seconds before retry; currently 60.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                },
                "example": 60
              }
            }
          },
          "503": {
            "description": "Service unavailable or guest feature disabled. Bounded retries apply only to transient failures; disabled features need configuration repair.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GuestError"
                },
                "examples": {
                  "typical": {
                    "value": {
                      "success": false,
                      "error": {
                        "code": "GUEST_SERVICE_UNAVAILABLE",
                        "retryable": true
                      }
                    }
                  }
                }
              }
            }
          },
          "413": {
            "description": "JSON body exceeds 8,000 bytes.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GuestError"
                },
                "examples": {
                  "typical": {
                    "value": {
                      "success": false,
                      "error": {
                        "code": "INVALID_GUEST_REQUEST",
                        "retryable": false
                      }
                    }
                  }
                }
              }
            }
          },
          "415": {
            "description": "Content-Type must be application/json.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GuestError"
                },
                "examples": {
                  "typical": {
                    "value": {
                      "success": false,
                      "error": {
                        "code": "INVALID_GUEST_REQUEST",
                        "retryable": false
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "summary": "Create, read, link or inspect usage of app-scoped guest profiles",
        "description": "Requires an app-scoped guest credential: guests:write for create, guests:read for read/usage, guests:link for link. Linking also requires current same-app OIDC access and grant with openid and guest:link, valid guest proof and explicit user approval. Limits are pooled across all developer apps: unclaimed guests are capped at 5 times included plan MAU (Developer 5,000; Growth 12,500; Pro 50,000; Scale 250,000), not measured active users. Guest capacity does not reset monthly. Guests do not consume registered MAU and guest access is currently unbilled. At capacity, stop new registrations, notify the developer and present the plan upgrade action; confirm available capacity before resuming. Existing reads, identical creation retries and authorized links remain available. Rate limit: 240 requests/minute per developer across apps. JSON request maximum: 8,000 bytes. No caller-supplied owner/app authority."
      }
    }
  },
  "externalDocs": {
    "url": "https://hybrid-id.com/integrations/guest-profiles.md"
  }
}
