This endpoint issues a new proof-of-human (PoH) credential to a holder of a valid World ID. It can re-verify with a Personal Custody Package (PCP) or issue a credential-only refresh when a PCP is not available.
Base URL is environment-specific and served by the signup-service app-api. Contact
your World ID point of contact for environment endpoints and access.
Endpoint
Content-Type: multipart/form-data
Request
| Header | Type | Required | Description |
|---|
x-zkp-proof | string | yes | Base64-encoded ZKP string containing idCommitment and sub. |
Query parameters
| Query | Type | Required | Description |
|---|
idComm | string | yes | User identity commitment. |
| Field | Type | Required | Description |
|---|
sub | string | yes | User account ID (hex with 0x prefix). Must match previous refreshes for this idComm. |
encrypted_user_id | string | no | Temporary field while operator rewards depend on it. |
Include all fields below when refreshing with a Personal Custody Package.
| Field | Type | Description |
|---|
signup_id | string | Signup ID. |
signup_id_salt | string | Signup ID salt. |
orb_id | string | Orb ID. |
orb_id_salt | string | Orb ID salt. |
operator_id | string | Operator ID. |
operator_id_salt | string | Operator ID salt. |
signup_reason | string | Signup reason. |
signup_reason_salt | string | Signup reason salt. |
timestamp | string | Timestamp. |
timestamp_salt | string | Timestamp salt. |
software_version | string | Software version. |
software_version_salt | string | Software version salt. |
orb_country | string | Orb country. |
orb_country_salt | string | Orb country salt. |
iris_code_shares_0 | string | Iris code share 0. |
iris_code_shares_1 | string | Iris code share 1. |
iris_code_shares_2 | string | Iris code share 2. |
hashes.json | file | PCP hashes JSON file. |
hashes.sign | file | PCP hashes signature file. |
Example (credential-only refresh)
curl -X POST "https://<app-api-host>/api/v1/refresh?idComm=0xabc123..." \
-H "x-zkp-proof: <base64-zkp>" \
-F "sub=0x1a2b3c"
Example (refresh with PCP)
curl -X POST "https://<app-api-host>/api/v1/refresh?idComm=0xabc123..." \
-H "x-zkp-proof: <base64-zkp>" \
-F "sub=0x1a2b3c" \
-F "signup_id=signup_123" \
-F "signup_id_salt=..." \
-F "orb_id=orb_abc" \
-F "orb_id_salt=..." \
-F "operator_id=operator_123" \
-F "operator_id_salt=..." \
-F "signup_reason=..." \
-F "signup_reason_salt=..." \
-F "timestamp=1700000000" \
-F "timestamp_salt=..." \
-F "software_version=1.2.3" \
-F "software_version_salt=..." \
-F "orb_country=US" \
-F "orb_country_salt=..." \
-F "iris_code_shares_0=..." \
-F "iris_code_shares_1=..." \
-F "iris_code_shares_2=..." \
-F "hashes.json=@hashes.json" \
-F "hashes.sign=@hashes.sign"
Response
Success response
{
"success": true,
"credential": "<base64-credential>",
"message": "Credential refreshed successfully"
}
Error responses
| Status | Error | Description |
|---|
| 400 | INVALID_SUB | sub is missing or invalid. |
| 400 | SUB_MISMATCH | sub does not match the one used in previous refreshes. |
| 404 | NO_SIGNUP_RECORD | No enrollment record found. User must re-enroll at an Orb. |
| 429 | RATE_LIMIT_EXCEEDED | Refresh rate limit exceeded for the current window. |
| 503 | - | Credential refresh is disabled. |
If PCP validation fails, the endpoint returns an error status with PCP_VALIDATION_FAILED.
The credential response field is a base64-encoded JSON representation of the World ID Credential object defined in world-id-protocol/crates/primitives/src/credential.rs.
Decoding
const decoded = JSON.parse(Buffer.from(credential, "base64").toString("utf8"));
Example (decoded)
{
"id": 123456789,
"version": "V1",
"issuer_schema_id": 42,
"sub": "<field-element-hex>",
"genesis_issued_at": 1733241600,
"expires_at": 1764777600,
"claims": ["<field-element-hex>", "<field-element-hex>", "<field-element-hex>"],
"associated_data_hash": "<field-element-hex>",
"signature": "<signature-hex>",
"issuer": {
"pk": ["<x-decimal>", "<y-decimal>"]
}
}
Field definitions
| Field | Type | Description |
|---|
id | uint64 | Issuer-scoped reference identifier for the credential. |
version | string | Credential version. Current value is V1. |
issuer_schema_id | uint64 | Identifier for the (issuer, schema) pair registered in CredentialSchemaIssuerRegistry. |
sub | FieldElement | Blinded subject identifier derived from the World ID leaf index and an issuer-specific blinding factor. |
genesis_issued_at | uint64 | Unix timestamp (seconds) of the first issuance of this credential. |
expires_at | uint64 | Unix timestamp (seconds) for expiration. |
claims | FieldElement[] | Up to 16 claim commitments. Unused indices are the zero field element. |
associated_data_hash | FieldElement | Poseidon2 hash of issuer-defined associated data. The associated data itself is not included. |
signature | string | 64-byte compressed EdDSA signature over the credential hash, hex-encoded (128 hex chars, no 0x). |
issuer | EdDSAPublicKey | Issuer public key that signed the credential. |
Field representations
- FieldElement values (
sub, claims, associated_data_hash) are hex strings with a 0x prefix and 64 hex characters.
- Issuer public key (
issuer.pk) is serialized as [x, y] decimal strings for BabyJubJub affine coordinates.
- Signature is hex-encoded compressed bytes (no
0x prefix).
PoH-specific claims
- When refreshing with a PCP,
claims[0] is derived from the PCP hashes.json bytes.
- When refreshing without a PCP,
claims[0] is derived from issuer-defined refresh data (a hash of idCommitment, sub, and a timestamp).
- In both cases,
associated_data_hash is instead empty, i.e. the zero field element
References