---
title: "Create a sandbox onboarding API key"
method: POST
path: "/onboarding/credentials/sandbox-key"
tags: ["Onboarding"]
---

# Create a sandbox onboarding API key

`POST /onboarding/credentials/sandbox-key`

## Request

- Base URL: `https://platform-api.homejourney.sg/v1`
- URL: `https://platform-api.homejourney.sg/v1/onboarding/credentials/sandbox-key`
- Auth: HTTP bearer

Creates a sandbox-only API key after current terms acceptance and credential self-service gates are complete. External URL: `/v1/onboarding/credentials/sandbox-key`. Current Next.js implementation path: `/api/platform/v1/onboarding/credentials/sandbox-key`. This mutating route requires `Idempotency-Key` and `credentials.write`; live credential creation remains operator-controlled. The raw key is revealed only on the initial successful response; replay-safe idempotency storage omits raw key material.

## Headers

- `X-Correlation-Id` string
- `Idempotency-Key` string, required

## Request body

- PlatformOnboardingSandboxCredentialRequest
  - `key_name` string, required: Human-readable sandbox key name shown in credential inventory.
  - `requested_scopes` PlatformScope[]: Optional approved self-service scope subset. The API rejects scope expansion and recursive credential-management scopes.
  - `expires_in_days` integer: Optional sandbox key lifetime in days. Defaults to 30.

## Response `200`

Sandbox credential metadata, one-time raw key reveal, and refreshed readiness contract.

- PlatformOnboardingSandboxCredentialResponse
  - `credential` object, required
    - `id` string, uuid, required
    - `key_name` string, required
    - `key_prefix` string, required
    - `environment` 'sandbox', required
    - `status` 'active', required
    - `scopes` PlatformScope[], required
    - `expires_at` string, date-time, required
    - `created_at` string, date-time, required
    - `last_used_at` string, date-time, nullable, required
    - `raw_api_key` string, nullable, required: Returned only on initial successful creation. Replay-safe idempotency responses set this to null because Homejourney never stores raw API keys.
    - `one_time_reveal` object, required
      - `available` boolean, required
      - `storage` 'not_stored', required
      - `replay_behavior` 'raw_key_omitted', required
  - `readiness` PlatformOnboardingReadinessResponse, required
    - `product` object, required
      - `id` 'service_provider_sandbox', required
      - `label` string, required
      - `target_actor` 'service_provider', required
      - `launch_stage` 'slice_1', required
      - `environment` 'sandbox' | 'live', required
      - `deployment_tier` 'local' | 'preview' | 'sandbox' | 'staging' | 'production', required
    - `organization` object, required
      - `id` string, uuid, required
      - `slug` string, required
      - `display_name` string, required
      - `type` string, required
    - `readiness` object, required
      - `state` 'sandbox_ready_live_blocked' | 'live_review_pending' | 'live_approved' | 'suspended', required
      - `sandbox_access` 'unavailable' | 'available' | 'suspended', required
      - `live_access` 'blocked_manual_review' | 'review_pending' | 'approved' | 'suspended', required
      - `credential_self_service` 'deferred_until_reviewed_slice' | 'sandbox_available' | 'live_blocked' | 'disabled', required
    - `application` object, required
      - `id` string, uuid, nullable, required
      - `product_id` 'service_provider_sandbox', required
      - `target_actor` 'service_provider', required
      - `state` 'draft' | 'terms_pending' | 'sandbox_ready' | 'live_review_pending' | 'live_approved' | 'suspended' | 'offboarded', required
      - `sandbox_access` 'unavailable' | 'available' | 'suspended', required
      - `live_access` 'blocked_manual_review' | 'review_pending' | 'approved' | 'suspended', required
      - `credential_self_service` 'deferred_until_reviewed_slice' | 'sandbox_available' | 'live_blocked' | 'disabled', required
      - `contract_package` object, required
        - `id` string, required
        - `version` string, required
      - `support_owner_email` string, email, nullable, required
      - `technical_owner_email` string, email, nullable, required
      - `submitted_at` string, date-time, nullable, required
      - `reviewed_at` string, date-time, nullable, required
      - `approved_at` string, date-time, nullable, required
      - `suspended_at` string, date-time, nullable, required
      - `updated_at` string, date-time, nullable, required
      - `source` 'platform.onboarding_applications' | 'computed_fallback', required
    - `contract_evidence` object[], required
      - `id` string, uuid, nullable, required
      - `contract_package` object, required
        - `id` string, required
        - `version` string, required
      - `signing_rail` 'clickwrap' | 'sign_with_singpass' | 'digital_signing_partner' | 'manual_review', required
      - `signature_status` 'not_started' | 'draft' | 'sent' | 'signed' | 'countersigned' | 'declined' | 'expired' | 'manual_review', required
      - `document_hash_algorithm` 'sha256' | 'null', nullable, required
      - `document_hash` string, nullable, required
      - `evidence_reference` string, nullable, required
      - `signer_display_name` string, nullable, required
      - `signer_role` string, nullable, required
      - `signer_authority_source` string, nullable, required
      - `signed_at` string, date-time, nullable, required
      - `countersigned_at` string, date-time, nullable, required
      - `expires_at` string, date-time, nullable, required
      - `source` 'platform.onboarding_contract_signatures' | 'computed_fallback', required
    - `contract_lifecycle` object, required
      - `state` 'terms_not_accepted' | 'signature_pending' | 'signature_recorded' | 'manual_review' | 'expired' | 'declined', required
      - `contract_package` object, required
        - `id` string, required
        - `version` string, required
        - `display_name` string, required
        - `status` 'active' | 'draft' | 'deprecated' | 'retired' | 'unregistered', required
        - `target_actor` 'service_provider' | 'bank_partner' | 'conveyancing_firm' | 'agent_agency' | 'advertiser' | 'landlord_owner', required
        - `allowed_environments` string[], required
        - `sandbox_terms_rail` 'clickwrap' | 'sign_with_singpass' | 'digital_signing_partner' | 'manual_review', required
        - `allowed_signing_rails` string[], required
        - `required_authority_sources` string[], required
        - `allowed_commercial_package_ids` string[], required
        - `default_commercial_package_id` 'sandbox_developer' | 'pilot_partner_api' | 'production_partner_api' | 'business_directory_advertiser_self_serve' | 'business_directory_advertiser_managed' | 'enterprise_bank_api' | 'internal_first_party', required
        - `allowed_live_scopes` string[], required
        - `requires_business_authority` boolean, required
        - `countersignature_required` boolean, required
        - `signing_session_available` boolean, required
        - `blocks_live_credentials_without_commercial_approval` true, required
        - `ai_agent_summary` string, required
        - `blocked_claims` string[], required
        - `source_spec` 'docs/features/platform/PLATFORM_CONTRACT_SIGNING_AND_COMMERCIAL_TERMS_SPEC.md', required
      - `signing_rails` object[], required
        - `rail` 'clickwrap' | 'sign_with_singpass' | 'digital_signing_partner' | 'manual_review', required
        - `availability` 'available' | 'blocked' | 'complete' | 'planned', required
        - `purpose` 'sandbox_terms_acceptance' | 'contract_signature', required
        - `human_approval_required` boolean, required
        - `reason` string, required
      - `business_authority` object, required
        - `required_for_live` true, required
        - `recorded` boolean, required
        - `accepted_sources` string[], required
        - `identity_rail_is_not_signature` true, required
      - `commercial_approval` object, required
        - `required_for_live` true, required
        - `recorded` boolean, required
        - `status` 'missing' | 'draft' | 'pending_review' | 'approved' | 'active' | 'suspended' | 'expired' | 'declined', required
        - `commercial_terms_id` string, nullable, required
        - `commercial_package_id` 'sandbox_developer' | 'pilot_partner_api' | 'production_partner_api' | 'business_directory_advertiser_self_serve' | 'business_directory_advertiser_managed' | 'enterprise_bank_api' | 'internal_first_party', required
        - `approval_reference` string, nullable, required
        - `billing_account_required` boolean, required
        - `billing_account_recorded` boolean, required
        - `approved_live_scope_count` integer, required
        - `effective_from` string, date-time, nullable, required
        - `effective_until` string, date-time, nullable, required
        - `source` 'platform.onboarding_commercial_terms' | 'computed_fallback', required
      - `commercial_packages` object, required
        - `sandbox` object, required
          - `id` 'sandbox_developer' | 'pilot_partner_api' | 'production_partner_api' | 'business_directory_advertiser_self_serve' | 'business_directory_advertiser_managed' | 'enterprise_bank_api' | 'internal_first_party', required
          - `display_name` string, required
          - `tier` 'sandbox_developer' | 'pilot_partner' | 'production_partner' | 'advertiser' | 'enterprise_bank' | 'internal_first_party', required
          - `overage_mode` 'non_billable' | 'observe_only' | 'manual_review' | 'finance_approved_only', required
          - `billing_account_required_for_live` boolean, required
          - `observe_only_launch` boolean, required
          - `support_tier` 'async' | 'named_owner' | 'production' | 'enterprise' | 'internal', required
          - `sla_posture` 'none' | 'internal_slo' | 'measured_internal' | 'contractual_deferred', required
          - `status_page_visibility` 'none' | 'internal' | 'partner_visible' | 'contract_specific', required
        - `target_live` object, required
          - `id` 'sandbox_developer' | 'pilot_partner_api' | 'production_partner_api' | 'business_directory_advertiser_self_serve' | 'business_directory_advertiser_managed' | 'enterprise_bank_api' | 'internal_first_party', required
          - `display_name` string, required
          - `tier` 'sandbox_developer' | 'pilot_partner' | 'production_partner' | 'advertiser' | 'enterprise_bank' | 'internal_first_party', required
          - `overage_mode` 'non_billable' | 'observe_only' | 'manual_review' | 'finance_approved_only', required
          - `billing_account_required_for_live` boolean, required
          - `observe_only_launch` boolean, required
          - `support_tier` 'async' | 'named_owner' | 'production' | 'enterprise' | 'internal', required
          - `sla_posture` 'none' | 'internal_slo' | 'measured_internal' | 'contractual_deferred', required
          - `status_page_visibility` 'none' | 'internal' | 'partner_visible' | 'contract_specific', required
      - `commercial_terms` object, required
        - `currency` 'SGD', required
        - `sandbox_billing` 'none' | 'non_billable' | 'observe_only_until_signed_package' | 'zero_unless_signed_package' | 'zero_until_usage_billing_package' | 'blocked_without_signed_package', required
        - `live_billing_mode` 'none' | 'non_billable' | 'observe_only_until_signed_package' | 'zero_unless_signed_package' | 'zero_until_usage_billing_package' | 'blocked_without_signed_package', required
        - `platform_access_fee` 'none' | 'non_billable' | 'observe_only_until_signed_package' | 'zero_unless_signed_package' | 'zero_until_usage_billing_package' | 'blocked_without_signed_package', required
        - `api_usage_fee` 'none' | 'non_billable' | 'observe_only_until_signed_package' | 'zero_unless_signed_package' | 'zero_until_usage_billing_package' | 'blocked_without_signed_package', required
        - `provider_onboarding_fee` 'none' | 'non_billable' | 'observe_only_until_signed_package' | 'zero_unless_signed_package' | 'zero_until_usage_billing_package' | 'blocked_without_signed_package', required
        - `default_marketplace_commission` 'none' | 'non_billable' | 'observe_only_until_signed_package' | 'zero_unless_signed_package' | 'zero_until_usage_billing_package' | 'blocked_without_signed_package', required
        - `legacy_commission_inheritance` 'none' | 'non_billable' | 'observe_only_until_signed_package' | 'zero_unless_signed_package' | 'zero_until_usage_billing_package' | 'blocked_without_signed_package', required
      - `live_gate` object, required
        - `requires_active_contract` true, required
        - `requires_business_authority` true, required
        - `requires_commercial_approval` true, required
        - `creates_live_credentials` false, required
        - `legacy_fee_terms_blocked` string[], required
      - `source_documents` string[], required
    - `state_machine` object, required
      - `current_state` 'draft' | 'terms_pending' | 'sandbox_ready' | 'live_review_pending' | 'live_approved' | 'suspended' | 'offboarded', required
      - `terminal_states` string[], required
      - `allowed_transitions` object[], required
        - `id` string, required
        - `from` 'draft' | 'terms_pending' | 'sandbox_ready' | 'live_review_pending' | 'live_approved' | 'suspended' | 'offboarded', required
        - `to` 'draft' | 'terms_pending' | 'sandbox_ready' | 'live_review_pending' | 'live_approved' | 'suspended' | 'offboarded', required
        - `requires_evidence` string[], required
        - `blocked_by_gate_ids` string[], required
        - `human_approval_required` boolean, required
        - `available` boolean, required
      - `last_event` object, nullable, required
        - `id` string, uuid, required
        - `event_type` string, required
        - `from_state` string, nullable, required
        - `to_state` string, required
        - `actor_type` string, required
        - `reason_code` string, nullable, required
        - `created_at` string, date-time, required
      - `source` 'platform.onboarding_applications' | 'computed_fallback', required
    - `evidence` object[], required
      - `id` string, required
      - `label` string, required
      - `status` 'complete' | 'missing' | 'blocked' | 'manual_review' | 'expired' | 'revoked', required
      - `owner` string, required
      - `source` string, required
      - `required_for` string[], required
      - `blocks_actions` string[], required
    - `missing_gates` object[], required
      - `id` string, required
      - `label` string, required
      - `status` 'complete' | 'missing' | 'blocked' | 'manual_review' | 'expired' | 'revoked', required
      - `owner` string, required
      - `source` string, required
      - `required_for` string[], required
      - `blocks_actions` string[], required
    - `allowed_actions` object[], required
      - `id` string, required
      - `label` string, required
      - `kind` 'read' | 'write' | 'external_handoff', required
      - `href` string, nullable, required
      - `sensitivity` 'public' | 'partner' | 'confidential' | 'regulated', required
      - `idempotency_required` boolean, required
      - `confirmation_required` boolean, required
      - `human_approval_required` boolean, required
    - `blocked_actions` object[], required
      - `id` string, required
      - `label` string, required
      - `kind` 'read' | 'write' | 'external_handoff', required
      - `href` string, nullable, required
      - `sensitivity` 'public' | 'partner' | 'confidential' | 'regulated', required
      - `idempotency_required` boolean, required
      - `confirmation_required` boolean, required
      - `human_approval_required` boolean, required
      - `reason` string, required
      - `missing_gate_ids` string[], required
    - `fallbacks` object[], required
      - `id` string, required
      - `label` string, required
      - `trigger` string, required
      - `support_path` string, required
    - `status_components` object[], required
      - `id` string, required
      - `label` string, required
      - `readiness` string, required
      - `truth_source` string, required
      - `partner_visible` boolean, required
      - `partner_status_visibility` 'authenticated_partner_readiness' | 'private_truth_only' | 'public_status_deferred', required
      - `public_status_component_id` 'partner_platform_api', required
      - `status_page_url` string, uri, required
      - `covered_surfaces` string[], required
      - `evidence_sources` string[], required
      - `public_status_blockers` string[], required
    - `support` object, required
      - `mode` 'pilot_email', required
      - `contact` 'support@homejourney.sg', required
      - `case_types` string[], required
    - `ai_agent_policy` object, required
      - `default_authority` 'read_and_draft_only', required
      - `mutating_actions_require_human_approval` true, required
      - `allowed_action_ids` string[], required
      - `blocked_action_ids` string[], required
      - `tool_contract` string, required
    - `consent_evidence` PlatformConsentEvidence
      - `grants` object, required
        - `total` integer, required
        - `active` integer, required
        - `revoked` integer, required
        - `superseded` integer, required
        - `suspended` integer, required
        - `expired` integer, required
      - `purposes` string[], required
      - `scopes` string[], required
      - `data_categories` string[], required
      - `retention` object, required
        - `earliest_until` string, date-time, nullable, required
        - `latest_until` string, date-time, nullable, required
      - `revocation` object, required
        - `revoked_count` integer, required
        - `latest_revoked_at` string, date-time, nullable, required
      - `audit` object, required
        - `recorded_event_count` integer, required
    - `livemode` boolean, required
    - `request_id` string, required
  - `idempotency_key_hash` string, required
  - `livemode` false, required
  - `request_id` string, required

## Other responses

- `400`: Request validation failed.
- `401`: Missing or invalid platform API key.
- `403`: Credential, environment, or scope is not allowed.
- `404`: Platform API feature or resource is not available.
- `409`: Idempotency key is in progress or conflicts with an earlier request body.
- `429`: Rate limit exceeded.
- `500`: Unexpected platform API failure.

---

[API](https://skmtc.dev/homejourney/apis/homejourney-platform-api.md) · [All operations](https://skmtc.dev/homejourney/apis/homejourney-platform-api/llms.txt) · [OpenAPI document](https://skmtc.dev/homejourney/apis/homejourney-platform-api/revisions/a2c6fe063db1?raw)
