Fac-360
Organizations

Mint an organization credential

OWNER, and an enrolled second factor. This is the credential a SaaS puts in a server to register its own client companies by API. THE FLOOR IS NOT ADMIN, and not by symmetry with anything: every other act an ADMIN performs lands inside their own organization, whereas companies:manage can register a company under ANY RUC in Peru, and core.companies.ruc is unique platform-wide, so claiming one DENIES it to whoever controls it -- a third party with no account here. SUNAT publishes no ownership oracle, so migration 000053's answer is attribution rather than prevention: every act writes an ops.audit_events row in the same transaction. A control whose whole mechanism is 'we can say who did it' belongs to the rank that can be held answerable. THE SECOND FACTOR IS CHECKED IN SQL, on auth.users.mfa_enrolled_at, because the failure this act invites is a stolen session -- which is by construction a caller of the correct rank, and would make the audit row name somebody who did nothing. Enrolment is self-service, so this strands nobody. THE TOKEN IS IN THE 201 AND NOWHERE ELSE.

POST
/console/v1/organizations/{organizationId}/credentials

OWNER, and an enrolled second factor. This is the credential a SaaS puts in a server to register its own client companies by API. THE FLOOR IS NOT ADMIN, and not by symmetry with anything: every other act an ADMIN performs lands inside their own organization, whereas companies:manage can register a company under ANY RUC in Peru, and core.companies.ruc is unique platform-wide, so claiming one DENIES it to whoever controls it -- a third party with no account here. SUNAT publishes no ownership oracle, so migration 000053's answer is attribution rather than prevention: every act writes an ops.audit_events row in the same transaction. A control whose whole mechanism is 'we can say who did it' belongs to the rank that can be held answerable. THE SECOND FACTOR IS CHECKED IN SQL, on auth.users.mfa_enrolled_at, because the failure this act invites is a stolen session -- which is by construction a caller of the correct rank, and would make the audit row name somebody who did nothing. Enrolment is self-service, so this strands nobody. THE TOKEN IS IN THE 201 AND NOWHERE ELSE.

Authorization

consoleSession
__Host-apf_console<token>

Set by POST /console/v1/sessions. HttpOnly, Secure, SameSite=Strict, Path=/, __Host- prefixed. It is never readable by JavaScript and there is no header alternative: accepting both carriers would let an attacker choose the weaker one.

In: cookie

Path Parameters

organizationId*string
Formatuuid

Request Body

application/json

TypeScript Definitions

Use the request body type in TypeScript.

Response Body

application/json

application/json

application/json

application/json

application/json

application/json

application/json

application/json

application/json

application/json

application/json

application/json

curl -X POST "https://example.com/console/v1/organizations/497f6eca-6276-4993-bfeb-53cbbbba6f08/credentials" \  -H "Authorization: Bearer apf_v2_tu_credencial" \  -H "Content-Type: application/json" \  -d '{    "name": "provisioning · staging",    "scopes": [      "organizations:read"    ],    "expiresInDays": 90  }'
{  "schemaVersion": "console.1",  "requestId": "d385ab22-0f51-4b97-9ecd-b8ff3fd4fcb6",  "credentialId": "f568fec0-10b6-4b94-9daf-e62c50c9bf3e",  "token": "string",  "name": "string",  "organizationId": "7bc05553-4b68-44e8-b7bc-37be63c6d9e9",  "organizationName": "string",  "scopes": [    "string"  ],  "expiresAt": "2019-08-24T14:15:22Z",  "createdAt": "2019-08-24T14:15:22Z",  "auditEventId": "b284fbf1-1a7a-4799-bddc-ba227ef5747f"}