Inyo

Sender

The Sender is the participant who initiates and pays for a transaction. Every sender must pass through KYC (Know Your Customer) screening, and their transaction limits are determined by their compliance level.


Initial Compliance Setup

At the beginning of your integration, the Inyo compliance team works with your organization to define a custom compliance framework tailored to your product and customer profile. This framework determines:

  • Compliance levels β€” tiers that define how much a customer can send within set timeframes (24h, 30d, 180d)
  • Validation rules β€” the required data and documents for each level (e.g., name, SSN, proof of income)
  • Risk controls β€” thresholds that trigger enhanced due diligence

This configuration is unique per tenant and directly impacts how participants are verified and which operations they can perform.


Endpoint: POST /organizations/{tenant}/v2/people
Authentication: Tenant-level (x-api-key)

⚠️ The v1 endpoint POST /organizations/{tenant}/people is deprecated. Use v2 β€” it takes the same payload and adds document validation and verification integrated into the KYC solution.

The v2 endpoint accepts the same request shape as v1 (see the field tables below), plus one additional per-document field β€” documents[].stateCode (issuing state/province, max 32 chars) β€” and applies a stricter, safer pipeline:

  • Rule-driven document validation β€” per country + document type, tenant-configured rules can require extra fields (e.g., stateCode for US driver's licenses) and enforce format regexes. Violations return 422 VALIDATION_ERROR with per-field errors under errors["documents.{i}.{field}"], and nothing is persisted.
  • Synchronous KYC number verification β€” where the rule requires it, the document number is verified against the Inyo360 KYC service during the request. A KYC rejection returns 422 with the rejection detail; if the KYC service is unavailable (or not configured for your tenant), the request fails closed with 422 KYC_UNAVAILABLE β€” retry with backoff, and if it persists, contact Inyo support.
  • Verification verdicts on record β€” verified documents carry kyc_verdict/kyc_verified_at on the person's document records.
  • Same idempotency as v1: if a person with the same externalId exists, the existing person is returned with 200; a new person returns 201.

No fields are technically required to create a person β€” but to use them as a sender, they must reach at least Compliance Level 1, which typically requires first name, last name, address, and phone number.

curl --request POST \
  --url https://{FQDN}/organizations/$TENANT/v2/people \
  --header 'Content-Type: application/json' \
  --header "x-api-key: $API_KEY" \
  --data '{
  "firstName": "John",
  "lastName": "Doe",
  "email": "[email protected]",
  "birthDate": "1990-01-15",
  "phoneNumber": "+15551234567",
  "gender": "Male",
  "externalId": "your-internal-id-001",
  "address": {
    "countryCode": "US",
    "stateCode": "CA",
    "city": "San Francisco",
    "line1": "123 Market St",
    "zipcode": "94105"
  },
  "documents": [
    {
      "type": "drivers_license",
      "document": "D12345678",
      "countryCode": "US",
      "stateCode": "CA",
      "expireDate": "2030-06-30"
    }
  ],
  "occupation": "Software Engineer"
}'

Request Fields

FieldTypeRequiredDescription
firstNamestringFor Level 1First name
lastNamestringFor Level 1Last name
phoneNumberstringFor Level 1Phone with country code (e.g., +15551234567)
emailstringFor US personsEmail address
genderstringFor US personsMale, Female, or Other
birthDatestringFor US personsFormat: yyyy-MM-dd
externalIdstringNoYour internal reference ID
addressobjectFor Level 1Residential address
address.countryCodestringYes (in address)ISO 3166-1 alpha-2
address.stateCodestringFor USUS state code (e.g., CA)
address.citystringYes (in address)City name
address.line1stringYes (in address)Street address
address.line2stringNoAdditional address info
address.zipcodestringYes (in address)Postal code
documentsarrayFor Level 2+Identity documents
documents[].typestringYes (in doc)Document type β€” see the accepted values below
documents[].documentstringYes (in doc)Document number
documents[].countryCodestringYes (in doc)Issuing country (ISO 3166-1 alpha-2)
documents[].stateCodestringPer rules (v2 only)Issuing state/province (max 32) β€” required by rule for some country/type combinations, e.g. US driver's licenses
documents[].expireDatestringNoExpiration date (yyyy-MM-dd). Also accepted as expiryDate; expireDate wins if both are sent.
documents[].issuerstringNoIssuing authority or state
occupationstringFor Level 2Person's occupation
employerNamestringNoEmployer name

Accepted Document Types

On the v2 endpoints, documents[].type uses lowercase values, following the same standard as the KYC solution:

v2 valuev1 value (deprecated)Description
passportPASSPORTPassport
drivers_licenseDRIVERS_LICENSEDriver's license
dniDNIDNI (Spain / Argentina / Peru)
ccCCCC (Colombia cΓ©dula de ciudadanΓ­a)
cpfCPFCPF (Brazil)
idIDGeneric government ID
consular_idCONSULAR_IDConsular ID (matrΓ­cula consular)
voter_idVOTER_IDVoter ID
ssnSSNUS Social Security number
itinITINUS ITIN (IRS)
otherOTHEROther government-issued document

Note the plural in the driver's-license value β€” v2 uses drivers_license and v1's canonical value is now DRIVERS_LICENSE, aligning with the KYC standard. Both APIs accept the legacy singular (DRIVER_LICENSE on v1, driver_license on v2) case-insensitively for backwards compatibility, but the plural is canonical: it's what's stored and what outbound DocumentUpdatedEvents webhooks emit.

These are declared document numbers β€” to verify document images, create a KYC verification session.

β“˜ The deprecated v1 PATCH /people/{personId} accepts a narrower set for documents[].type (PASSPORT, DRIVERS_LICENSE, DNI, CC, CPF, ID, SSN, ITIN; the singular DRIVER_LICENSE is also accepted) and restricts documents[].countryCode to US, BR, CO, PE, KE. The v2 PATCH /v2/people/{personId} has no such restriction β€” its document rules mirror v2 create exactly.

Sample Response

{
  "id": "48066496-9445-41b7-acbe-85e069a77cb7",
  "firstName": "John",
  "lastName": "Doe",
  "mainAddressId": "9c2ea7e5-51a2-4ea1-83cf-8948754486f8",
  "phoneNumber": "+15551234567",
  "email": "[email protected]",
  "gender": "Male",
  "birthDate": "1990-01-15",
  "externalId": "your-internal-id-001",
  "updatedAt": "2025-01-15T12:00:00",
  "documents": [],
  "occupation": "Software Engineer",
  "documentId": null,
  "sourceOfFundsId": null,
  "employerName": null,
  "employerAddressId": null
}

Save the returned id β€” this is the senderId used in all subsequent API calls.

Behavior notes:

  • Idempotent on externalId β€” if a person with the same externalId already exists in your tenant, create returns the existing person (200) instead of creating a duplicate.
  • Senders must be 18+ β€” birthDate is rejected if the person would be under 18.
  • Document updates are number-aware on PATCH β€” if the submitted documents[].type + documents[].document match an existing active document, that row is updated in place (expiry, issuer, state, country, and KYC verdicts refreshed). If the document number differs (or there's no prior document of that type), the previous row is retired and a new one is inserted, preserving the compliance audit trail.
  • expiryDate is accepted as an alias for expireDate on create and update. If both are present, the explicit expireDate wins.

Endpoint: PATCH /organizations/{tenant}/v2/people/{personId}
Authentication: Tenant-level

⚠️ The v1 endpoint PATCH /organizations/{tenant}/people/{personId} is deprecated. The v2 update applies the same rule-driven validation and synchronous KYC number verification as v2 create, and its document rules mirror create exactly (v1's update accepted a narrower document set).

Only fields included in the request body are updated; omitted fields remain unchanged. For documents, an update to an existing document number is applied in place rather than duplicated β€” see the note under Creating a Sender. Re-sending the same document with refreshed metadata (e.g. a corrected expireDate) updates the existing record instead of piling up duplicates.

curl --request PATCH \
  --url https://{FQDN}/organizations/$TENANT/v2/people/$PERSON_ID \
  --header 'Content-Type: application/json' \
  --header "x-api-key: $API_KEY" \
  --data '{
  "occupation": "Consultant",
  "documents": [
    {
      "type": "drivers_license",
      "document": "D87654321",
      "countryCode": "US",
      "stateCode": "CA",
      "expireDate": "2031-01-31"
    }
  ]
}'

Address Verification

Use the address check endpoint to pre-validate an address before creating or updating a sender. It applies the same country-aware validation rules (including per-country zipcode formats) as the real save endpoints, so a passing check here means the address will be accepted on POST /people.

curl --request GET \
  --url "https://{FQDN}/organizations/$TENANT/addresses/check?countryCode=US&stateCode=CA&city=San+Francisco&line1=123+Market+St&zipcode=94105" \
  --header "x-api-key: $API_KEY"

Query parameters: line1, city, stateCode, and countryCode are required; zipcode is validated against the destination country's format rules.

Response CodeMeaning
200Address is valid β€” the response includes { "valid": true, "normalized": { ... } }
422Validation failed β€” VALIDATION_ERROR with a per-field breakdown

Creating Business Senders (KYB)

For B2B use cases, you can create a company as a sender:

Endpoint: POST /organizations/{tenant}/companies
Authentication: Tenant-level

Companies follow a similar compliance level system but with different required fields (business registration, EIN, etc.). Contact your Inyo account manager for your tenant-specific KYB configuration.


Compliance Levels

LevelTypical Required FieldsDescription
Level 0(none)Cannot transact
Level 1firstName, lastName, address, phoneNumberBasic KYC
Level 2SSN, occupation, document IDEnhanced KYC
Level 3Proof of source of fundsFull KYC

These are customizable per tenant. See Trust Level Limits for details.


Best Practices

  • Progressive onboarding β€” Collect only Level 1 fields at signup. Prompt for more data when the user needs higher limits.
  • Pre-validate addresses β€” Use the address check endpoint before creating the sender to avoid holds.
  • Sync compliance state β€” After profile updates, re-check the compliance level to see if it has upgraded.
  • Handle restricted users β€” If a sender is Restricted, check GET /participants/{id}/complianceLevels to understand why and what action is needed.

All Endpoints

OperationMethodEndpoint
Create person (v2)POST/organizations/{tenant}/v2/people
Update person (v2)PATCH/organizations/{tenant}/v2/people/{personId}
Create KYC verification sessionPOST/organizations/{tenant}/v2/people/{personId}/kycSession
Create person (v1 β€” deprecated)POST/organizations/{tenant}/people
Update person (v1 β€” deprecated)PATCH/organizations/{tenant}/people/{personId}
Get personGET/organizations/{tenant}/people/{personId}
Get person with detailsGET/organizations/{tenant}/people/{personId}/details
Update addressPUT/organizations/{tenant}/people/{personId}/address
Update employer addressPUT/organizations/{tenant}/people/{personId}/employerAddress
Update place of birthPUT/organizations/{tenant}/people/{personId}/placeOfBirth
Check addressGET/organizations/{tenant}/addresses/check