API contracts / App Service v2 / Devices

Register or update this device

OperationregisterDevice
Call
PUT /gateway/urvi-app-service/api/v2/devices/{deviceId}
Group
Devices
Features
Device registration and remote actions, App startup and role detection
Replaces today

Use case. Called after login, on every app update and when device details change. Registers the app-scoped device for the user (status LIVE) with brand, model, OS and app version and, optionally, the FCM token. When the tenant enforces the one-device rule and another device is bound, returns 409 DEVICE_BINDING_CONFLICT with the other device; the app shows the conflict dialog (Check Sync / Login here / Logout).

Rules.

  • deviceId must equal X-Device-Id and the token's device claim.

Error codes. DEVICE_BINDING_CONFLICT, DEVICE_BLOCKED

Parameters

NameInTypeRequiredMeaningExample
X-App-VersionheaderstringYesApp version `major.minor.patch+build`. Too old -> 426 `VERSION_TOO_OLD`. · pattern ^\d+\.\d+\.\d+(\+\d+)?$2.0.0+200
X-Device-Idheaderstring (uuid)YesApp-scoped device UUID (not ANDROID_ID).6b1f2c3d-0000-4000-8000-00000000d001
X-Request-Idheaderstring (uuid)NoClient-generated request id for tracing; echoed in responses and problems.0e0c5a1b-0000-4000-8000-0000000000a1
Accept-LanguageheaderstringNoLanguage for messages and labels. · one of en, te, ta; default "en"te
X-TenantheaderstringNoTenant code; must match the token. Urvi: `ap.gov`. · one of ap.govap.gov
deviceIdpathstring (uuid)YesApp-scoped device UUID; must equal `X-Device-Id`.6b1f2c3d-0000-4000-8000-00000000d001
Idempotency-Keyheaderstring (uuid)YesUUID per write request; retained >= 30 days. Reusing a key with a different body -> 422 `IDEMPOTENCY_KEY_REUSED`.9c8b7a6d-0000-4000-8000-0000000000b1

Request

Body schema DeviceRegistration.

Example request body
{
  "brand": "BrandA",
  "model": "ModelA",
  "osVersion": "14",
  "appVersion": "2.0.0+200",
  "pushToken": "fcm-token-EXAMPLE-0001",
  "locale": "te"
}
FieldTypeRequiredRulesMeaning
brandstringYesmax length 50
modelstringYesmax length 50
osVersionstringYesmax length 20
appVersionstringYesmax length 20
pushTokenstringNomax length 4096FCM registration token.
localestringNoone of en, te, ta

Responses

200 Registered.

Schema Device.

Example 200 body
{
  "deviceId": "6b1f2c3d-0000-4000-8000-000000053249",
  "brand": "BrandA",
  "model": "ModelA",
  "osVersion": "14",
  "appVersion": "2.0.0+200",
  "status": "LIVE",
  "boundAt": "2025-06-01T08:00:00+05:30",
  "lastSeenAt": "2025-10-14T09:30:00+05:30"
}
FieldTypeRequiredRulesMeaning
deviceIdstring (uuid)Yes
brandstringNo
modelstringNo
osVersionstringNo
appVersionstringNo
statusstringNoone of LIVE, EXPIRED, BLOCKED; server-set
boundAtstring (date-time)Noserver-set
lastSeenAtstring (date-time)Noserver-set

400 Malformed request or schema violation.

Schema Problem.

Example 400 body
{
  "type": "https://urvi.example.invalid/problems/validation-failed",
  "title": "Validation failed",
  "status": 400,
  "detail": "One or more fields are invalid.",
  "code": "VALIDATION_FAILED",
  "requestId": "0e0c5a1b-0000-4000-8000-0000000000a1",
  "errors": [
    {
      "field": "/mobile",
      "code": "INVALID_MOBILE",
      "message": "Mobile must be 10 digits starting 6-9."
    }
  ]
}

401 Missing, invalid or expired access token. Refresh once, then ask the user to log in; pending outbox ops stay intact.

Schema Problem.

Example 401 body
{
  "type": "https://urvi.example.invalid/problems/token-expired",
  "title": "Session expired",
  "status": 401,
  "detail": "The access token has expired.",
  "code": "TOKEN_EXPIRED",
  "requestId": "0e0c5a1b-0000-4000-8000-0000000000a1"
}

403 Authenticated but not allowed (role, tenant, device binding or feature switch).

Schema Problem.

Example 403 body
{
  "type": "https://urvi.example.invalid/problems/role-not-allowed",
  "title": "Not allowed",
  "status": 403,
  "detail": "Your role cannot perform this action.",
  "code": "ROLE_NOT_ALLOWED",
  "requestId": "0e0c5a1b-0000-4000-8000-0000000000a1"
}

409 Another device is bound.

Schema Problem.

Example 409 body
{
  "type": "https://urvi.example.invalid/problems/device-binding-conflict",
  "title": "Device session conflict",
  "status": 409,
  "detail": "You are logged in on another device.",
  "code": "DEVICE_BINDING_CONFLICT",
  "requestId": "0e0c5a1b-0000-4000-8000-0000000000a1",
  "context": {
    "otherDevice": {
      "deviceId": "6b1f2c3d-0000-4000-8000-000000053250",
      "brand": "BrandB",
      "model": "ModelB",
      "osVersion": "13",
      "appVersion": "2.0.0+200",
      "status": "LIVE",
      "boundAt": "2025-10-10T08:00:00+05:30"
    }
  }
}

426 App version below the minimum; block the UI with an update prompt. Pending outbox ops are kept.

Schema Problem.

Example 426 body
{
  "type": "https://urvi.example.invalid/problems/version-too-old",
  "title": "Update required",
  "status": 426,
  "detail": "Please update Urvi to continue.",
  "code": "VERSION_TOO_OLD",
  "requestId": "0e0c5a1b-0000-4000-8000-0000000000a1",
  "minAppVersion": "2.0.0"
}

429 Rate limited per device / user. Respect `Retry-After`.

Schema Problem.

Example 429 body
{
  "type": "https://urvi.example.invalid/problems/rate-limited",
  "title": "Too many requests",
  "status": 429,
  "detail": "Slow down and retry later.",
  "code": "RATE_LIMITED",
  "requestId": "0e0c5a1b-0000-4000-8000-0000000000a1",
  "retryAfterSeconds": 30
}

500 Unexpected server error; retry with backoff (writes are idempotent).

Schema Problem.

Example 500 body
{
  "type": "https://urvi.example.invalid/problems/internal-error",
  "title": "Server error",
  "status": 500,
  "detail": "Something went wrong. Please try again.",
  "code": "INTERNAL_ERROR",
  "requestId": "0e0c5a1b-0000-4000-8000-0000000000a1"
}