e-Próspera

Partner Keys

Partner Keys let an approved organization create and manage natural-person residency applications over REST. Each `pk-...` key belongs to one Partner Integration and can access only that integration's applications.

View as Markdown

The actors have separate responsibilities: the partner manages application data, private proof uploads, payment, status, and submission. The authenticated applicant personally accepts the Agreement of Coexistence (AOC) and completes Veriff inside eProspera. Partners cannot submit a signature, start Veriff, receive a Veriff URL, or read Veriff documents or decisions through this API.

Base URL

EnvironmentOrigin
Productionhttps://portal.eprospera.com
Staginghttps://staging-portal.eprospera.com

Provisioning and key lifecycle

Partner integrations and keys are issued by eProspera administrators. They are not self-service.

  • The raw key is displayed once. eProspera stores only its SHA-256 hash.
  • Keys expire after 90 days.
  • Issuing a replacement key shortens older active keys to a maximum seven-day overlap.
  • A key can be revoked immediately. Integrations can be suspended or revoked independently.
  • Authentication is rejected when the key is expired or revoked, the integration is not active, or its legal entity is dissolved.
  • Redirect and checkout return URLs must use an HTTPS origin approved on the integration.

Send the key on every request:

Authorization: Bearer pk-...

Scopes

ScopeCapability
partner:person.application.readList and read this integration's applications and private uploads
partner:person.application.createCreate applications and upload proof of address
partner:person.application.updateUpdate Draft applications and upload proof of address
partner:person.application.payCreate checkout sessions and apply voucher payments
partner:person.application.submitSubmit a completed application

There is no API scope for signing the Agreement of Coexistence. Applicant consent always occurs on an eProspera-hosted page.

Endpoints

MethodPathRequired scope
POST/api/v1/partner/residency_applicationspartner:person.application.create
GET/api/v1/partner/residency_applicationspartner:person.application.read
GET/api/v1/partner/residency_applications/{id}partner:person.application.read
PATCH/api/v1/partner/residency_applications/{id}partner:person.application.update
POST/api/v1/uploads/proof_of_addresscreate or update
GET/api/v1/uploads/proof_of_address/{uploadId}read, create, or update
POST/api/v1/partner/residency_applications/{id}/checkout_sessionpartner:person.application.pay
POST/api/v1/partner/residency_applications/{id}/pay/voucherpartner:person.application.pay
POST/api/v1/partner/residency_applications/{id}/pay/couponpartner:person.application.pay (deprecated alias)
POST/api/v1/partner/residency_applications/{id}/submitpartner:person.application.submit

Cross-partner reads and mutations return 404 so the existence of another integration's resources is not disclosed.

Secure application flow

Partner and applicant responsibilities

StagePartner through the Partner APIApplicant inside eProspera
ApplicationCreates the Draft and updates data using expectedVersion
Proof and paymentUploads private proof of address and completes checkout or voucher payment
Account accessReads applicantPortalAccess status only; never receives the claim tokenClaims the account from the 24-hour email and logs in
AOC acceptanceFetches and delivers the current agreementUrl; the partner cannot signAuthenticates as the application owner and signs only on eProspera
VeriffReads verificationRequired and submitReady only; cannot initiate Veriff or view its dataStarts and completes Veriff in the authenticated portal
Submission and statusSubmits when submitReady is true, then polls application status
  1. Create a Draft with POST /partner/residency_applications and a unique Idempotency-Key. eProspera sends the applicant a single-use account-claim link that expires after 24 hours.
  2. Choose one proof path: upload a document using the new application ID, or send applicationData.personalDetails.proofOfAddressSwornStatement inline on create/PATCH.
  3. For the upload path only, attach the returned opaque proofOfAddressUploadId with PATCH /partner/residency_applications/{id} and the latest expectedVersion. The sworn-statement path skips upload and attachment.
  4. Pay with a checkout session or voucher. Each payment request requires its own Idempotency-Key.
  5. Instruct the applicant to use the claim email and log in to eProspera. Fetch the application again after the final PATCH, then send that applicant the current nextSteps.agreementUrl.
  6. The agreement URL contains a 30-minute, single-use token. eProspera requires the applicant's Auth0 login and verifies that the logged-in subject owns the application. During login, the bearer is held server-side behind a short-lived, single-use opaque continuation rather than copied into login or returnTo URLs. A different user receives 404, and admin impersonation is not accepted as applicant identity.
  7. After acceptance, fetch the application again: acceptance consumes the link and increments version. Use the refreshed version for any later PATCH.
  8. The logged-in applicant starts and completes Veriff in the authenticated eProspera portal. The partner sees only applicantPortalAccess, nextSteps.verificationRequired, and nextSteps.submitReady.
  9. Submit the application after nextSteps.submitReady becomes true.

The hosted agreement page selects the applicable AOC server-side. Acceptance records the agreement ID and version, SHA-256 hashes of its content and the legally relevant application data, the authenticated applicant's Auth0 subject, signer name, signature image, timestamp, IP address, user agent, and access-token ID. A legally relevant PATCH invalidates the current acceptance and returns a new agreementUrl; submission also rejects consent if application data changed through another path. Bearer-only acceptances without a matching authenticated signer are not valid for submission.

Submission requires current agreement acceptance, proof of address, a paid invoice, and approved identity verification. AOC acceptance and payment endpoints never submit the application; submission occurs only through /submit with partner:person.application.submit.

Create and update contracts

Create requests require Idempotency-Key:

curl -X POST https://portal.eprospera.com/api/v1/partner/residency_applications \
  -H "Authorization: Bearer pk-REDACTED" \
  -H "Idempotency-Key: application-ada-20260716" \
  -H "Content-Type: application/json" \
  -d '{
    "applicationData": {
      "residencyType": "e_resident",
      "personalDetails": {
        "givenName": "Ada",
        "surname": "Lovelace",
        "email": "applicant@example.com",
        "dateOfBirth": "1990-12-10",
        "sex": "female",
        "countryOfBirth": "GB",
        "citizenships": ["GB"],
        "alternateNamesAttestation": false,
        "criminalBackgroundAttestation": true,
        "address": {
          "line1": "123 Main St",
          "city": "London",
          "postalCode": "SW1A 1AA",
          "country": "GB"
        },
        "phoneNumber": "+447700900123"
      }
    },
    "redirectUrl": "https://partner.example/return"
  }'

An update sends the full application DTO plus the response's latest version:

{
  "expectedVersion": 3,
  "applicationData": {
    "residencyType": "e_resident",
    "personalDetails": {
      "givenName": "Ada",
      "surname": "Lovelace",
      "email": "applicant@example.com",
      "dateOfBirth": "1990-12-10",
      "sex": "female",
      "countryOfBirth": "GB",
      "citizenships": ["GB"],
      "alternateNamesAttestation": false,
      "criminalBackgroundAttestation": true,
      "proofOfAddressUploadId": "12345678-abcd-4abc-8abc-123456789012",
      "address": {
        "line1": "123 Main St",
        "city": "London",
        "postalCode": "SW1A 1AA",
        "country": "GB"
      },
      "phoneNumber": "+447700900123"
    }
  }
}

Once an invoice is paid, the residency type and applicant email are locked. A stale expectedVersion returns 409 with currentVersion.

Response fields

Application responses include:

FieldMeaning
versionInteger used as the next PATCH expectedVersion
applicationDataCurated application data
applicationData.personalDetails.proofOfAddressUploadIdOpaque private-upload ID, when an upload is attached
applicantPortalAccessClaim/login metadata; never contains credentials or a Veriff URL
nextSteps.agreementUrlHosted consent URL while current acceptance is required
nextSteps.proofOfAddressRequiredWhether proof is still missing
nextSteps.paymentRequiredWhether payment is still required
nextSteps.verificationRequiredWhether portal identity verification is still required
nextSteps.verificationInstructionsApplicant-facing portal verification guidance, when required
nextSteps.submitReadyWhether all submission prerequisites currently pass

nextSteps.agreementUrl is ephemeral. Fetch the application after every PATCH and after AOC acceptance. Do not cache or reuse an older URL or version.

applicantPortalAccess.claimLinkSent and claimLinkSentAt describe whether the current mutation enqueued a claim email. GET requests do not send email and therefore return false and null for those fields.

Application lists use cursor pagination:

GET /api/v1/partner/residency_applications?limit=50&cursor=<application-id>

limit defaults to 50 and cannot exceed 100. The response contains pagination.nextCursor, or null on the final page.

Idempotency

Application creation, checkout creation, and voucher payment require an Idempotency-Key containing 8–128 URL-safe characters. Records are retained for 24 hours per integration and route.

  • An identical completed request replays the stored response with Idempotency-Replayed: true.
  • Reusing a key with different input returns 409.
  • A duplicate received while the original is processing returns 409.

Rate limits

Limits are enforced by both API key and integration:

ClassPer keyPer integration
Reads120/minute300/minute
Updates and submission60/minute150/minute
Application creation10/minute50/hour
Uploads20/minute100/hour
Payment and vouchers5/minute20/hour

Invalid credentials are limited to 20 attempts per minute per source IP. A throttled request returns 429 and a Retry-After header.

Errors

StatusMeaning
400Invalid request, disallowed redirect origin, or invalid state
401Missing, invalid, expired, or revoked key; inactive integration
403Valid key without the required scope
404Resource missing or owned by another integration
409Stale application version or idempotency conflict
429Rate limit exceeded; use Retry-After

Every successful action, denied scoped action, throttling decision, inactive-key attempt, and admin key lifecycle change is durably audited without storing raw credentials.

On this page