{
  "openapi" : "3.1.0",
  "info" : {
    "title" : "Plumma CONNECT API",
    "description" : "**Plumma CONNECT** is a phone-intelligence aggregation service. A single call\ntakes a phone number in E.164 form plus a list of **commands** (SIM-swap\ncheck, KYC match, current carrier, porting timestamp, …), routes each command\nto the best available supplier for the number's country, and returns one\naggregated response.\n\n## Authentication\nEvery call must carry the `x-plumma-connect-api-key` header. Its value is your\n**X.509 client certificate, base64-encoded**.\n\nTwo different refusals, and they call for two different actions:\n\n- **`401`** — the certificate itself could not be used: unreadable, expired,\n  or not yet valid. A new key is what fixes it.\n- **`403`** — the certificate is fine and the call is not allowed: the header\n  was not sent at all, or the certificate was issued for another purpose, or\n  the application it names is not one this key is admitted to. A new key\n  changes nothing here.\n\nBoth answer `application/problem+json`, and the `detail` field says which of\nthe cases it is in a sentence meant to be read.\n\n## Live or sandbox: `x-plumma-connect-app-id` decides\nThe second header is what tells the two apart, and it is not optional in\npractice — a live call without it does not fail, it silently becomes a\nsandbox call.\n\n- **Sent** — the call runs **live**: real suppliers, real data, billed per\n  served command. The pair (your certificate, that application) must be\n  **admitted**; if it is not, the call is refused with `403` and nothing is\n  billed. Admission is granted by Plumma per application, and an application\n  you have just created is not admitted until it is.\n- **Not sent** — the call runs in the **sandbox**: canned identities, nothing\n  billed, and it may only ask about a **demo number**. Any other number is\n  refused with `401`.\n\nThe application id is the one shown for that application in the Plumma\nconsole. There is no way to ask for the live engine any other way: the\nrequest cannot name its own role.\n\n## Billing (per served command)\nYou are charged **per command a supplier actually served**, never per API\ncall. A command whose country is not covered, whose supplier call failed, or\nthat was rejected before routing is **not** billed. A supplier answer that\nmeans \"I hold no data for this\" (e.g. a KYC score of `-1`) is a served result\nand **is** billed.\n\n## Correlation id\nNever send `X-Correlation-ID` — the server generates it and returns it on the\nresponse header of every call (success or error). Quote it in support\nrequests: it identifies your call server-side.\n\n## Model version\n`info.version` matches the model set (PlmRequest / PlmResponse) this contract\ndescribes. The **same** string is returned in the `version` field of every\n`PlmResponse`, so you can confirm the service matches this document at runtime:\nmake any call and compare `response.version` with `info.version` here.\n\nA standalone JSON Schema for request validation ships alongside this file at\n`plmrequest.schema.json` — point your validator at it to check a payload\nbefore sending.\n",
    "contact" : {
      "name" : "PLUMMA SRL",
      "email" : "info@plumma.it"
    },
    "license" : {
      "name" : "Proprietary — © PLUMMA SRL, all rights reserved."
    },
    "version" : "1.0.2-20260903172027",
    "x-model-version" : "1.0.2-20260903172027"
  },
  "servers" : [ {
    "url" : "https://connect.plumma.it/services",
    "description" : "Production"
  } ],
  "tags" : [ {
    "name" : "CONNECT",
    "description" : "The flagship aggregation endpoint."
  } ],
  "paths" : {
    "/api" : {
      "post" : {
        "tags" : [ "CONNECT" ],
        "summary" : "Run one or more intelligence commands against a phone number",
        "operationId" : "connectApi",
        "description" : "Submit a phone number and a non-empty list of commands. The response is a\nsingle `PlmResponse` in which only the blocks relevant to the served\ncommands are populated (unpopulated fields are omitted, not null).\n\n**Validation happens before routing.** A malformed payload returns `400`\nwith the offending field named in `detail`; no supplier is contacted and\nnothing is billed.\n",
        "security" : [ {
          "ApiKeyAuth" : [ ]
        } ],
        "parameters" : [ {
          "$ref" : "#/components/parameters/ApplicationId"
        } ],
        "requestBody" : {
          "required" : true,
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/PlmRequest"
              },
              "examples" : {
                "simSwap" : {
                  "summary" : "SIM-swap check (single command)",
                  "value" : {
                    "number" : "+393273339145",
                    "commands" : [ "sim_swap" ]
                  }
                },
                "kycMatchStructured" : {
                  "summary" : "KYC match with a structured address",
                  "value" : {
                    "number" : "+393273339145",
                    "commands" : [ "kyc_match" ],
                    "kyc_challenges" : {
                      "name" : {
                        "first_name" : "Luigi",
                        "last_name" : "Armani"
                      },
                      "dob" : {
                        "day" : 28,
                        "month" : 2,
                        "year" : 1999
                      },
                      "address" : {
                        "street" : "Via Corsini",
                        "street_no" : "21",
                        "city" : "Fanano",
                        "province" : "MO",
                        "postcode" : "41021",
                        "country" : "IT"
                      }
                    }
                  }
                },
                "kycMatchInline" : {
                  "summary" : "KYC match with a free-text (inline) address",
                  "value" : {
                    "number" : "+447808226974",
                    "commands" : [ "kyc_match" ],
                    "kyc_challenges" : {
                      "name" : {
                        "first_name" : "John",
                        "last_name" : "Smith"
                      },
                      "inline_address" : {
                        "address" : "23 Omnia Street, London, EC1A 1BB, GB",
                        "normalize" : true
                      }
                    }
                  }
                },
                "wideCall" : {
                  "summary" : "Several commands at once",
                  "value" : {
                    "number" : "+447808226974",
                    "commands" : [ "current_carrier", "line_classification", "sim_swap" ]
                  }
                }
              }
            }
          }
        },
        "responses" : {
          "200" : {
            "description" : "Routing completed. Inspect `status` / `status_message` for the outcome\nand the populated blocks for the data. HTTP 200 does not by itself mean\nevery command was served — read `status`.\n",
            "headers" : {
              "X-Correlation-ID" : {
                "$ref" : "#/components/headers/CorrelationId"
              }
            },
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/PlmResponse"
                },
                "examples" : {
                  "simSwapServed" : {
                    "summary" : "SIM-swap served (the operator sent the date of the last swap)",
                    "value" : {
                      "number" : "+393273339145",
                      "simswap" : {
                        "last_day" : 0,
                        "risk_indicator" : 4,
                        "simswap_min_threshold" : 720,
                        "date" : "2018-10-17T23:00:00.000Z",
                        "swapped" : false,
                        "swapped_max_age" : 240
                      },
                      "status" : 0,
                      "status_message" : "Response from one supplier",
                      "version" : "1.0.2-20260704190444"
                    }
                  },
                  "simSwapNoSwapFound" : {
                    "summary" : "SIM-swap served (0 = the operator watched and found no swap)",
                    "description" : "The operator holds no swap for this number, and sent no date to place one with. `swapped` / `swapped_max_age` carry the bounded claim - nothing in the last 240 hours - while the absent thresholds say there is no swap to bound. Not to be confused with `-1`, which is the operator having nothing to answer with at all.",
                    "value" : {
                      "number" : "+5581986179310",
                      "simswap" : {
                        "last_day" : 0,
                        "risk_indicator" : 0,
                        "swapped" : false,
                        "swapped_max_age" : 240
                      },
                      "status" : 0,
                      "status_message" : "Response from one supplier",
                      "version" : "1.0.2-20260704190444"
                    }
                  },
                  "simSwapNotApplicable" : {
                    "summary" : "SIM-swap served (-1 = the operator holds nothing for this identifier)",
                    "description" : "The number is well formed and the operator answered `422 SERVICE_NOT_APPLICABLE` - a landline, an M2M or data-only SIM, an MVNO that has not enabled the API. An answer, so the command is served and billed; a call that got no answer would leave the whole `simswap` block absent instead.",
                    "value" : {
                      "number" : "+5519992858171",
                      "simswap" : {
                        "risk_indicator" : -1
                      },
                      "status" : 0,
                      "status_message" : "Response from one supplier",
                      "version" : "1.0.2-20260704190444"
                    }
                  },
                  "kycMatchServed" : {
                    "summary" : "KYC match served (0 = no match, -1 = operator holds no data)",
                    "value" : {
                      "number" : "+393273339145",
                      "kyc_results" : {
                        "first_name_score" : 0,
                        "last_name_score" : 0,
                        "name_score" : 0,
                        "dob_score" : 0,
                        "country_score" : -1
                      },
                      "status" : 0,
                      "status_message" : "Response from one supplier",
                      "version" : "1.0.2-20260704190444"
                    }
                  }
                }
              }
            }
          },
          "400" : {
            "description" : "Malformed or invalid request payload — `detail` names the offending field.",
            "headers" : {
              "X-Correlation-ID" : {
                "$ref" : "#/components/headers/CorrelationId"
              }
            },
            "content" : {
              "application/problem+json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/ProblemDetail"
                },
                "example" : {
                  "type" : "about:blank",
                  "title" : "Bad Request",
                  "status" : 400,
                  "detail" : "Plumma kyc_challenges must have either address or inline_address populated",
                  "instance" : "/services/api",
                  "path" : "/services/api",
                  "correlation_id" : "8b3489e2-dab3-4b1f-a448-2ab46071fac8"
                }
              }
            }
          },
          "401" : {
            "description" : "The certificate could not be used: not a readable X.509 certificate,\nor expired, or not yet valid. `detail` says which. Mint a new key.\n",
            "headers" : {
              "X-Correlation-ID" : {
                "$ref" : "#/components/headers/CorrelationId"
              }
            },
            "content" : {
              "application/problem+json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/ProblemDetail"
                }
              }
            }
          },
          "403" : {
            "description" : "The certificate is usable and the call is still not allowed: the\n`x-plumma-connect-api-key` header was absent, or the certificate was\nissued for a purpose Connect does not accept, or it is not admitted to\nthe application named in `x-plumma-connect-app-id`. `detail` says\nwhich. A new key does not help — the admission has to change.\n",
            "headers" : {
              "X-Correlation-ID" : {
                "$ref" : "#/components/headers/CorrelationId"
              }
            },
            "content" : {
              "application/problem+json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/ProblemDetail"
                }
              }
            }
          },
          "500" : {
            "description" : "Unexpected server error. `detail` is generic by design — use the\n`correlation_id` to locate the failure in the logs.\n",
            "headers" : {
              "X-Correlation-ID" : {
                "$ref" : "#/components/headers/CorrelationId"
              }
            },
            "content" : {
              "application/problem+json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/ProblemDetail"
                }
              }
            }
          }
        }
      }
    }
  },
  "components" : {
    "securitySchemes" : {
      "ApiKeyAuth" : {
        "type" : "apiKey",
        "in" : "header",
        "name" : "x-plumma-connect-api-key",
        "description" : "Your X.509 client certificate, base64-encoded."
      }
    },
    "parameters" : {
      "ApplicationId" : {
        "name" : "x-plumma-connect-app-id",
        "in" : "header",
        "required" : false,
        "description" : "The application this call belongs to, as shown in the Plumma console.\n\n**Present** — the call runs against the live suppliers as that\napplication, and the pair (certificate, application) has to be admitted:\nit is refused with `403` otherwise, and a value that is not a number is\nrefused the same way.\n\n**Absent** — the call runs in the sandbox and may only ask about a demo\nnumber. Nothing is billed, and no admission is needed.\n\nOmitting it is therefore not a smaller version of a live call: it is a\ndifferent call, answered from canned data.\n",
        "schema" : {
          "type" : "string",
          "pattern" : "^[0-9]+$"
        },
        "example" : "11"
      }
    },
    "headers" : {
      "CorrelationId" : {
        "description" : "Server-generated request id, echoed on every response. Never sent by the client.",
        "schema" : {
          "type" : "string",
          "format" : "uuid",
          "example" : "8b3489e2-dab3-4b1f-a448-2ab46071fac8"
        }
      }
    },
    "schemas" : {
      "ProblemDetail" : {
        "type" : "object",
        "description" : "RFC 7807 problem document returned on every 4xx/5xx.",
        "properties" : {
          "type" : {
            "type" : "string",
            "format" : "uri",
            "example" : "about:blank"
          },
          "title" : {
            "type" : "string",
            "example" : "Bad Request"
          },
          "status" : {
            "type" : "integer",
            "format" : "int32",
            "example" : 400
          },
          "detail" : {
            "type" : "string",
            "example" : "Plumma commands list cannot be null or empty"
          },
          "instance" : {
            "type" : "string",
            "example" : "/services/api"
          },
          "path" : {
            "type" : "string",
            "example" : "/services/api"
          },
          "correlation_id" : {
            "type" : "string",
            "format" : "uuid",
            "example" : "8b3489e2-dab3-4b1f-a448-2ab46071fac8"
          }
        }
      },
      "Address" : {
        "type" : "object",
        "properties" : {
          "street" : {
            "type" : "string",
            "minLength" : 1,
            "maxLength" : 100,
            "examples" : [ "Via Corsini" ]
          },
          "street_no" : {
            "type" : "string",
            "maxLength" : 10,
            "pattern" : "[A-Za-z0-9\\-/]+",
            "examples" : [ "21" ]
          },
          "city" : {
            "type" : "string",
            "minLength" : 1,
            "maxLength" : 50,
            "examples" : [ "Fanano" ],
            "$comment" : "Server enforces [\\p{L}'\\-\\s]+ ; omitted here for cross-engine portability."
          },
          "province" : {
            "type" : "string",
            "maxLength" : 50,
            "examples" : [ "MO" ]
          },
          "postcode" : {
            "type" : "string",
            "pattern" : "[0-9A-Za-z]{4,10}",
            "examples" : [ "41021" ]
          },
          "national_id" : {
            "type" : "string",
            "maxLength" : 50,
            "pattern" : "[A-Za-z0-9\\-]+"
          },
          "country" : {
            "type" : "string",
            "pattern" : "[A-Za-z]{2,3}",
            "description" : "ISO country code (2-3 letters).",
            "examples" : [ "IT" ]
          },
          "normalize" : {
            "type" : "boolean",
            "default" : false
          },
          "house_number_extension" : {
            "type" : "string"
          }
        },
        "additionalProperties" : false,
        "description" : "Structured postal address. Mutually exclusive with inline_address.",
        "required" : [ "street", "street_no", "city", "postcode", "country" ]
      },
      "Dob" : {
        "type" : "object",
        "properties" : {
          "day" : {
            "type" : "integer",
            "minimum" : 1,
            "maximum" : 31,
            "examples" : [ 28 ]
          },
          "month" : {
            "type" : "integer",
            "minimum" : 1,
            "maximum" : 12,
            "examples" : [ 2 ]
          },
          "year" : {
            "type" : "integer",
            "minimum" : 1900,
            "maximum" : 2100,
            "examples" : [ 1999 ]
          }
        },
        "additionalProperties" : false,
        "description" : "Date of birth. Either fully specified (day+month+year) or entirely omitted — a partial DOB is rejected.",
        "dependencies" : {
          "day" : [ "month", "year" ],
          "month" : [ "day", "year" ],
          "year" : [ "day", "month" ]
        }
      },
      "InlineAddress" : {
        "type" : "object",
        "properties" : {
          "address" : {
            "type" : "string",
            "minLength" : 1,
            "examples" : [ "23 Omnia Street, London, EC1A 1BB, GB" ]
          },
          "normalize" : {
            "type" : "boolean",
            "default" : false
          }
        },
        "additionalProperties" : false,
        "description" : "Free-text address as a single string. Mutually exclusive with address.",
        "required" : [ "address" ]
      },
      "KycChallenges" : {
        "type" : "object",
        "properties" : {
          "dob" : {
            "$ref" : "#/components/schemas/Dob"
          },
          "name" : {
            "$ref" : "#/components/schemas/Name"
          },
          "address" : {
            "$ref" : "#/components/schemas/Address"
          },
          "email" : {
            "type" : "string",
            "format" : "email",
            "examples" : [ "john.smith@example.com" ]
          },
          "inline_address" : {
            "$ref" : "#/components/schemas/InlineAddress"
          },
          "return_address" : {
            "type" : "boolean",
            "description" : "Ask the supplier to return the normalized address block."
          },
          "gender" : {
            "type" : "string"
          }
        },
        "additionalProperties" : false,
        "description" : "Data to match for `kyc_match`. Must carry EXACTLY ONE address form: `address` (structured) or `inline_address` (free text) — never both, never neither.",
        "oneOf" : [ {
          "$comment" : "structured address present, inline absent",
          "required" : [ "address" ],
          "not" : {
            "required" : [ "inline_address" ]
          }
        }, {
          "$comment" : "inline address present, structured absent",
          "required" : [ "inline_address" ],
          "not" : {
            "required" : [ "address" ]
          }
        } ]
      },
      "Name" : {
        "type" : "object",
        "properties" : {
          "first_name" : {
            "type" : "string",
            "minLength" : 2,
            "maxLength" : 100,
            "$comment" : "Server also requires ^[\\p{L}][\\p{L}'\\-\\s]*$; \\p{L} is not portable across JSON Schema regex engines, so only length is validated here and the exact character rule stays server-side."
          },
          "last_name" : {
            "type" : "string",
            "minLength" : 2,
            "maxLength" : 100
          },
          "middle_name" : {
            "type" : "string",
            "minLength" : 2,
            "maxLength" : 100
          },
          "name_kana_hankaku" : {
            "type" : "string"
          },
          "name_kana_zenkaku" : {
            "type" : "string"
          },
          "family_name_at_birth" : {
            "type" : "string",
            "minLength" : 2,
            "maxLength" : 100
          }
        },
        "additionalProperties" : false
      },
      "allowedCommandValues" : {
        "type" : "string",
        "enum" : [ "line_classification", "current_carrier", "issuing_carrier", "porting_timestamp", "porting_logs", "network_presence", "roaming_intel", "deactivation_point", "churn_tracker", "sim_swap", "port_fraud_shield", "digital_footprint", "age_verification", "kyc_match", "divert_detector", "commercial_segment", "tenure_period" ]
      },
      "PlmRequest" : {
        "type" : "object",
        "properties" : {
          "number" : {
            "type" : "string",
            "minLength" : 1,
            "pattern" : "^\\+?[1-9][0-9]{6,14}$",
            "description" : "Phone number in international form. Accepted with or without a leading '+'; the server normalises to E.164 and rejects numbers libphonenumber considers invalid (a regex cannot fully replicate that check).",
            "examples" : [ "+393273339145", "447808226974" ]
          },
          "commands" : {
            "type" : "array",
            "items" : {
              "$ref" : "#/components/schemas/allowedCommandValues"
            },
            "minItems" : 1,
            "description" : "Non-empty list of commands to run. Duplicate/unknown values are rejected (400)."
          },
          "kyc_challenges" : {
            "$ref" : "#/components/schemas/KycChallenges"
          },
          "tenure_period" : {
            "type" : "integer",
            "description" : "Tenure window in days. Required only when the `tenure_period` command is requested.",
            "examples" : [ 90 ]
          }
        },
        "additionalProperties" : false,
        "required" : [ "number", "commands" ],
        "allOf" : [ {
          "$comment" : "When kyc_match is requested, kyc_challenges is required.",
          "if" : {
            "properties" : {
              "commands" : {
                "contains" : {
                  "const" : "kyc_match"
                }
              }
            },
            "required" : [ "commands" ]
          },
          "then" : {
            "required" : [ "kyc_challenges" ]
          }
        }, {
          "$comment" : "When the tenure_period command is requested, the tenure_period value is required.",
          "if" : {
            "properties" : {
              "commands" : {
                "contains" : {
                  "const" : "tenure_period"
                }
              }
            },
            "required" : [ "commands" ]
          },
          "then" : {
            "required" : [ "tenure_period" ]
          }
        } ]
      },
      "AgeVerification" : {
        "type" : "object",
        "properties" : {
          "verified" : {
            "type" : "integer"
          },
          "threshold" : {
            "type" : "integer"
          }
        }
      },
      "ChurnTracker" : {
        "type" : "object",
        "properties" : {
          "operator" : {
            "type" : "string"
          },
          "action" : {
            "type" : "string"
          },
          "ts" : {
            "type" : "string",
            "format" : "date-time",
            "description" : "When the deactivation/churn event happened, ISO-8601 UTC.",
            "examples" : [ "2025-11-02T08:30:00.000Z" ]
          },
          "number" : {
            "type" : "integer"
          },
          "number2" : {
            "type" : "integer"
          }
        }
      },
      "CurrentCarrier" : {
        "type" : "object",
        "properties" : {
          "lrn" : { },
          "mcc" : {
            "type" : "string"
          },
          "mnc" : {
            "type" : "string"
          },
          "name" : {
            "type" : "string"
          },
          "spid" : {
            "type" : "string"
          },
          "ocn" : { }
        }
      },
      "DigitalFootprint" : {
        "type" : "object",
        "properties" : {
          "whatsapp" : {
            "type" : "integer"
          },
          "telegram" : {
            "type" : "integer"
          },
          "amazon" : {
            "type" : "integer"
          },
          "google" : {
            "type" : "integer"
          },
          "office365" : {
            "type" : "integer"
          },
          "instagram" : {
            "type" : "integer"
          },
          "linkedin" : {
            "type" : "integer"
          },
          "twitter" : {
            "type" : "integer"
          },
          "skype" : {
            "type" : "integer"
          },
          "flipkart" : {
            "type" : "integer"
          },
          "viber" : {
            "type" : "integer"
          },
          "bukalapak" : {
            "type" : "integer"
          },
          "facebook" : {
            "type" : "integer"
          },
          "error" : {
            "type" : "integer"
          },
          "error_description" : {
            "type" : "string"
          }
        }
      },
      "DivertDetector" : {
        "type" : "object",
        "properties" : {
          "unconditional_call_forward" : {
            "type" : "integer"
          }
        }
      },
      "IssuingCarrier" : {
        "type" : "object",
        "properties" : {
          "mcc" : {
            "type" : "string"
          },
          "mnc" : {
            "type" : "string"
          },
          "name" : {
            "type" : "string"
          },
          "spid" : {
            "type" : "string"
          },
          "ocn" : { }
        }
      },
      "KycResults" : {
        "type" : "object",
        "properties" : {
          "first_name_score" : {
            "type" : "integer"
          },
          "last_name_score" : {
            "type" : "integer"
          },
          "middle_name_score" : {
            "type" : "integer"
          },
          "name_score" : {
            "type" : "integer"
          },
          "address_score" : {
            "type" : "integer"
          },
          "street_no_score" : {
            "type" : "integer"
          },
          "street_score" : {
            "type" : "integer"
          },
          "city_score" : {
            "type" : "integer"
          },
          "postcode_score" : {
            "type" : "integer"
          },
          "dob_score" : {
            "type" : "integer"
          },
          "province_score" : {
            "type" : "integer"
          },
          "country_score" : {
            "type" : "integer"
          },
          "national_id_score" : {
            "type" : "integer"
          },
          "email_score" : {
            "type" : "integer"
          }
        }
      },
      "NormalizedAddress" : {
        "type" : "object",
        "properties" : {
          "street_no" : {
            "type" : "string"
          },
          "street" : {
            "type" : "string"
          },
          "city" : {
            "type" : "string"
          },
          "province" : {
            "type" : "string"
          },
          "postcode" : {
            "type" : "string"
          },
          "country" : {
            "type" : "string"
          }
        }
      },
      "PortingLogs" : {
        "type" : "object",
        "properties" : {
          "plmnetwork" : {
            "type" : "integer"
          },
          "i_type" : {
            "type" : "string"
          },
          "action" : {
            "type" : "string"
          },
          "ts" : {
            "type" : "string",
            "format" : "date-time",
            "description" : "When the porting event happened, ISO-8601 UTC.",
            "examples" : [ "2016-03-11T09:15:00.000Z" ]
          }
        }
      },
      "QdrHistory" : {
        "type" : "object",
        "properties" : {
          "service" : {
            "type" : "string"
          },
          "source" : {
            "type" : "string"
          },
          "ts" : {
            "type" : "string",
            "format" : "date-time",
            "description" : "When the query-detail record was observed, ISO-8601 UTC.",
            "examples" : [ "2026-08-01T07:00:00.000Z" ]
          },
          "plmnetwork" : {
            "type" : "string"
          }
        }
      },
      "RoamingNetwork" : {
        "type" : "object",
        "properties" : {
          "lrn" : { },
          "mcc" : {
            "type" : "string"
          },
          "mnc" : {
            "type" : "string"
          },
          "name" : {
            "type" : "string"
          },
          "spid" : {
            "type" : "string"
          },
          "ocn" : { }
        }
      },
      "Simswap" : {
        "type" : "object",
        "properties" : {
          "last_day" : {
            "type" : "integer",
            "description" : "1 when the last SIM change falls within the last 24 hours, 0 otherwise. Absent when neither can be stated: when the operator answered only that a swap happened somewhere inside a window wider than 24 h, and when it refused or held nothing."
          },
          "risk_indicator" : {
            "type" : "integer",
            "description" : "0 = the operator watched and found no SIM swap, the lowest risk there is. 1 = swap in the last 24 h, 2 = last 72 h, 3 = last 30 days, 4 = older than 30 days, which is stated only when the operator's own monitored period is at least that long. Two negatives are answers, not failures: -1 = the operator answered that it holds nothing servable for this number, -2 = the operator answered refusing on this number. A command whose call got no answer leaves the whole block absent instead - which is the difference between an operator with nothing to say and a supplier that never replied.",
            "examples" : [ 0 ]
          },
          "simswap_min_threshold" : {
            "type" : "integer",
            "description" : "Lower bound, in hours, of the window the last SIM change falls into. Absent when there is no lower bound (the change is more recent than 24 h) and when there is no swap to bound at all (`risk_indicator` 0)."
          },
          "simswap_max_threshold" : {
            "type" : "integer",
            "description" : "Upper bound, in hours, of the window the last SIM change falls into. Absent when there is no upper bound (the change is older than the monitored period) and when there is no swap to bound at all (`risk_indicator` 0). When the operator answered with the boolean alone, this is the window that boolean referred to."
          },
          "date" : {
            "type" : "string",
            "format" : "date-time",
            "description" : "When the last SIM change happened, ISO-8601 UTC. Absent when the supplier answered but holds no date.",
            "examples" : [ "2018-10-17T23:00:00.000Z" ]
          },
          "swapped" : {
            "type" : "boolean",
            "description" : "Whether a SIM swap happened within the last `swapped_max_age` hours. Meaningless without that window, which is why the two always travel together. Absent when neither the date nor the supplier's own answer allowed it to be determined.",
            "examples" : [ false ]
          },
          "swapped_max_age" : {
            "type" : "integer",
            "description" : "The window, in hours, that `swapped` refers to.",
            "examples" : [ 240 ]
          }
        },
        "description" : "SIM-swap block. `date` and the `risk_indicator` / threshold trio are the supplier's own view of the last SIM change; `swapped` / `swapped_max_age` are the CAMARA pair and answer one fixed question, so two suppliers can be compared."
      },
      "PlmResponse" : {
        "type" : "object",
        "properties" : {
          "number" : {
            "type" : "string"
          },
          "kyc_results" : {
            "$ref" : "#/components/schemas/KycResults"
          },
          "normalize_address" : {
            "type" : "string"
          },
          "current_carrier" : {
            "$ref" : "#/components/schemas/CurrentCarrier"
          },
          "ported" : {
            "type" : "boolean"
          },
          "issuing_carrier" : {
            "$ref" : "#/components/schemas/IssuingCarrier"
          },
          "ported_date" : {
            "type" : "string",
            "format" : "date-time",
            "description" : "When the number was ported to its current carrier, ISO-8601 UTC. Suppliers that only hold the calendar day are rendered at midnight UTC; `ported_date_type` declares how precise the underlying date is.",
            "examples" : [ "2016-03-11T00:00:00.000Z" ]
          },
          "ported_date_type" : {
            "type" : "string",
            "description" : "How precise `ported_date` is, as declared by the supplier that answered.",
            "examples" : [ "FULL" ]
          },
          "present" : {
            "type" : "string"
          },
          "type" : {
            "type" : "string"
          },
          "etype" : {
            "type" : "string"
          },
          "porting_logs" : {
            "type" : "array",
            "items" : {
              "$ref" : "#/components/schemas/PortingLogs"
            },
            "description" : "Porting events known for the number. Absent when the command was not requested or no supplier held any row — never an empty array."
          },
          "is_roaming" : {
            "type" : "string"
          },
          "roaming_network" : {
            "$ref" : "#/components/schemas/RoamingNetwork"
          },
          "portfraud" : {
            "type" : "string"
          },
          "qdr_history" : {
            "type" : "array",
            "items" : {
              "$ref" : "#/components/schemas/QdrHistory"
            }
          },
          "status" : {
            "type" : "integer"
          },
          "status_message" : {
            "type" : "string"
          },
          "simswap" : {
            "$ref" : "#/components/schemas/Simswap"
          },
          "plm_score" : {
            "type" : "integer"
          },
          "age_verification" : {
            "$ref" : "#/components/schemas/AgeVerification"
          },
          "digital_footprint" : {
            "$ref" : "#/components/schemas/DigitalFootprint"
          },
          "divert_detector" : {
            "$ref" : "#/components/schemas/DivertDetector"
          },
          "churn_tracker" : {
            "type" : "array",
            "items" : {
              "$ref" : "#/components/schemas/ChurnTracker"
            },
            "description" : "Deactivation/churn events known for the number, most useful as a recency signal. Absent when the command was not requested or no supplier held any row — never an empty array."
          },
          "normalized_address" : {
            "$ref" : "#/components/schemas/NormalizedAddress"
          },
          "market_segment" : {
            "type" : "string"
          },
          "version" : {
            "type" : "string",
            "readOnly" : true,
            "description" : "The model-set version that produced this response. Compare it with `info.version` of this document to confirm the service matches the contract you are reading."
          }
        }
      }
    }
  }
}
