> For the complete documentation index, see [llms.txt](https://docs.thewalletcrew.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.thewalletcrew.io/api-reference/tenant-configuration/security.md).

# Security

## Generate JWT tokens from custom claims

> Creates one or more JWT tokens with custom claims for advanced authentication scenarios.\
> \
> \## Use Cases\
> \- Testing and development\
> \- Service-to-service authentication\
> \- Custom identity workflows\
> \- Token migration scenarios\
> \
> \## Validity Duration\
> \- Default: 10 years (3650.00:00:00.000)\
> \- Configurable via query parameter in TimeSpan format\
> \- Examples: \<c>1.00:00:00\</c> (1 day), \<c>30.00:00:00\</c> (30 days)\
> \
> \## Request Format\
> Accepts an array of claim sets, where each set generates one token:\
> \<c>\[\[{\\"type\\": \\"sub\\", \\"value\\": \\"user123\\"}, {\\"type\\": \\"email\\", \\"value\\": \\"<user@example.com>\\"}]]\</c>\
> \
> \## Security Warning\
> Generated tokens have full authentication authority. Protect endpoint access and token distribution carefully.

```json
{"openapi":"3.1.1","info":{"title":"Neostore internal API","version":"v1"},"tags":[{"name":"Security"}],"servers":[{"url":"https://app.neostore.cloud","description":"Production Server"},{"url":"https://app-qa.neostore.cloud","description":"Staging Server"}],"security":[{"admin-bearer":["tenant.authenticationToken:write"]},{"apiKey":[]}],"components":{"securitySchemes":{"admin-bearer":{"type":"oauth2","flows":{"implicit":{"authorizationUrl":"https://auth.neostore.cloud/authorize?audience=https://app.neostore.cloud/api/","scopes":{}}}},"apiKey":{"type":"apiKey","name":"X-API-KEY","in":"header"}},"schemas":{"WebClaim":{"type":"object","properties":{"type":{"type":"string","description":"claim type"},"value":{"type":"string","description":"Value"},"valueType":{"type":["null","string"],"description":"optional value type"}},"additionalProperties":false,"description":"Claim to generate JWT token for"},"ProblemDetails":{"type":"object","properties":{"type":{"type":["null","string"]},"title":{"type":["null","string"]},"status":{"type":["null","integer"],"format":"int32"},"detail":{"type":["null","string"]},"instance":{"type":["null","string"]}},"additionalProperties":{}},"HttpValidationProblemDetails":{"type":"object","allOf":[{"$ref":"#/components/schemas/ProblemDetails"}],"properties":{"errors":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}}},"additionalProperties":{}}}},"paths":{"/api/{tenantId}/tokens/generate":{"post":{"tags":["Security"],"summary":"Generate JWT tokens from custom claims","description":"Creates one or more JWT tokens with custom claims for advanced authentication scenarios.\n\n## Use Cases\n- Testing and development\n- Service-to-service authentication\n- Custom identity workflows\n- Token migration scenarios\n\n## Validity Duration\n- Default: 10 years (3650.00:00:00.000)\n- Configurable via query parameter in TimeSpan format\n- Examples: <c>1.00:00:00</c> (1 day), <c>30.00:00:00</c> (30 days)\n\n## Request Format\nAccepts an array of claim sets, where each set generates one token:\n<c>[[{\\\"type\\\": \\\"sub\\\", \\\"value\\\": \\\"user123\\\"}, {\\\"type\\\": \\\"email\\\", \\\"value\\\": \\\"user@example.com\\\"}]]</c>\n\n## Security Warning\nGenerated tokens have full authentication authority. Protect endpoint access and token distribution carefully.","parameters":[{"name":"validityDuration","in":"query","description":"Optional validity duration in TimeSpan format (e.g., \"1.00:00:00\" for 1 day). Default is 10 years","schema":{"type":"string"}},{"name":"tenantId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"description":"Array of claim sets; each set generates one JWT token","content":{"application/json":{"schema":{"type":"array","items":{"type":"array","items":{"description":"Claim to generate JWT token for","$ref":"#/components/schemas/WebClaim"}}}},"text/json":{"schema":{"type":"array","items":{"type":"array","items":{"description":"Claim to generate JWT token for","$ref":"#/components/schemas/WebClaim"}}}},"application/*+json":{"schema":{"type":"array","items":{"type":"array","items":{"description":"Claim to generate JWT token for","$ref":"#/components/schemas/WebClaim"}}}}}},"responses":{"200":{"description":"Tokens generated successfully.","content":{"text/plain":{"schema":{"type":"array","items":{"type":"string"}}},"application/json":{"schema":{"type":"array","items":{"type":"string"}}},"text/json":{"schema":{"type":"array","items":{"type":"string"}}}}},"401":{"description":"Caller not authenticated.","content":{"text/plain":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}},"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}},"text/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}}}},"403":{"description":"Caller lacks AuthenticationToken.Write scope.","content":{"text/plain":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}},"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}},"text/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}}}},"500":{"description":"Unexpected server error."}}}}}}
```

## Get the check-in policy

> Returns the \`checkInSecurityMode\` of \`security.yml\`. A tenant whose file does not carry one\
> gets \`full\`, the policy the runtime applies in that case.

```json
{"openapi":"3.1.1","info":{"title":"Neostore internal API","version":"v1"},"tags":[{"name":"Security"}],"servers":[{"url":"https://app.neostore.cloud","description":"Production Server"},{"url":"https://app-qa.neostore.cloud","description":"Staging Server"}],"security":[{"admin-bearer":["tenant.configuration.enrolment:read"]},{"apiKey":[]}],"components":{"securitySchemes":{"admin-bearer":{"type":"oauth2","flows":{"implicit":{"authorizationUrl":"https://auth.neostore.cloud/authorize?audience=https://app.neostore.cloud/api/","scopes":{}}}},"apiKey":{"type":"apiKey","name":"X-API-KEY","in":"header"}},"schemas":{"CheckinPolicyDefinition":{"required":["mode"],"type":"object","properties":{"mode":{"description":"Policy stored in `security.yml`. `full` when the file does not carry one, which is what\nthe runtime applies in that case.","$ref":"#/components/schemas/CheckinPolicyMode"}},"additionalProperties":false,"description":"Check-in policy currently applied by this tenant."},"CheckinPolicyMode":{"enum":["noIdentification","none","partial","full"],"type":"string","description":"Identification required from a customer collecting a pass."},"ProblemDetails":{"type":"object","properties":{"type":{"type":["null","string"]},"title":{"type":["null","string"]},"status":{"type":["null","integer"],"format":"int32"},"detail":{"type":["null","string"]},"instance":{"type":["null","string"]}},"additionalProperties":{}},"HttpValidationProblemDetails":{"type":"object","allOf":[{"$ref":"#/components/schemas/ProblemDetails"}],"properties":{"errors":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}}},"additionalProperties":{}}}},"paths":{"/api/{tenantId}/admin/security/checkinPolicy":{"get":{"tags":["Security"],"summary":"Get the check-in policy","description":"Returns the `checkInSecurityMode` of `security.yml`. A tenant whose file does not carry one\ngets `full`, the policy the runtime applies in that case.","parameters":[{"name":"tenantId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Check-in policy returned.","content":{"text/plain":{"schema":{"description":"Check-in policy currently applied by this tenant.","$ref":"#/components/schemas/CheckinPolicyDefinition"}},"application/json":{"schema":{"description":"Check-in policy currently applied by this tenant.","$ref":"#/components/schemas/CheckinPolicyDefinition"}},"text/json":{"schema":{"description":"Check-in policy currently applied by this tenant.","$ref":"#/components/schemas/CheckinPolicyDefinition"}}}},"404":{"description":"The tenant has no readable `security.yml`.","content":{"text/plain":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}},"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}},"text/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}}}}}}}}}
```

## Configure the check-in policy

> Replaces the \`checkInSecurityMode\` of \`security.yml\`. Every other section of the file, the\
> general secrets included, is written back unchanged.

```json
{"openapi":"3.1.1","info":{"title":"Neostore internal API","version":"v1"},"tags":[{"name":"Security"}],"servers":[{"url":"https://app.neostore.cloud","description":"Production Server"},{"url":"https://app-qa.neostore.cloud","description":"Staging Server"}],"security":[{"admin-bearer":["tenant.configuration.enrolment:write"]},{"apiKey":[]}],"components":{"securitySchemes":{"admin-bearer":{"type":"oauth2","flows":{"implicit":{"authorizationUrl":"https://auth.neostore.cloud/authorize?audience=https://app.neostore.cloud/api/","scopes":{}}}},"apiKey":{"type":"apiKey","name":"X-API-KEY","in":"header"}},"schemas":{"UpdateCheckinPolicyRequest":{"required":["mode"],"type":"object","properties":{"mode":{"description":"Identification required from a customer collecting a pass.","$ref":"#/components/schemas/CheckinPolicyMode"}},"additionalProperties":false,"description":"Request body to configure the check-in policy."},"CheckinPolicyMode":{"enum":["noIdentification","none","partial","full"],"type":"string","description":"Identification required from a customer collecting a pass."},"CheckinPolicyDefinition":{"required":["mode"],"type":"object","properties":{"mode":{"description":"Policy stored in `security.yml`. `full` when the file does not carry one, which is what\nthe runtime applies in that case.","$ref":"#/components/schemas/CheckinPolicyMode"}},"additionalProperties":false,"description":"Check-in policy currently applied by this tenant."},"ProblemDetails":{"type":"object","properties":{"type":{"type":["null","string"]},"title":{"type":["null","string"]},"status":{"type":["null","integer"],"format":"int32"},"detail":{"type":["null","string"]},"instance":{"type":["null","string"]}},"additionalProperties":{}},"HttpValidationProblemDetails":{"type":"object","allOf":[{"$ref":"#/components/schemas/ProblemDetails"}],"properties":{"errors":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}}},"additionalProperties":{}}}},"paths":{"/api/{tenantId}/admin/security/checkinPolicy":{"put":{"tags":["Security"],"summary":"Configure the check-in policy","description":"Replaces the `checkInSecurityMode` of `security.yml`. Every other section of the file, the\ngeneral secrets included, is written back unchanged.","parameters":[{"name":"tenantId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"description":"Request body to configure the check-in policy.","$ref":"#/components/schemas/UpdateCheckinPolicyRequest"}},"text/json":{"schema":{"description":"Request body to configure the check-in policy.","$ref":"#/components/schemas/UpdateCheckinPolicyRequest"}},"application/*+json":{"schema":{"description":"Request body to configure the check-in policy.","$ref":"#/components/schemas/UpdateCheckinPolicyRequest"}}},"required":true},"responses":{"200":{"description":"Check-in policy updated.","content":{"text/plain":{"schema":{"description":"Check-in policy currently applied by this tenant.","$ref":"#/components/schemas/CheckinPolicyDefinition"}},"application/json":{"schema":{"description":"Check-in policy currently applied by this tenant.","$ref":"#/components/schemas/CheckinPolicyDefinition"}},"text/json":{"schema":{"description":"Check-in policy currently applied by this tenant.","$ref":"#/components/schemas/CheckinPolicyDefinition"}}}},"400":{"description":"The policy is not one of the supported values.","content":{"text/plain":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}},"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}},"text/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}}}},"404":{"description":"The tenant has no readable `security.yml`.","content":{"text/plain":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}},"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}},"text/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}}}}}}}}}
```

## Get the general secrets

> Returns \`secret1\` and \`secret2\` of \`security.yml\` in clear — they are the values an\
> integrator signs \`externalIdentifier\` HMACs with, so there is nothing useful to return redacted.

```json
{"openapi":"3.1.1","info":{"title":"Neostore internal API","version":"v1"},"tags":[{"name":"Security"}],"servers":[{"url":"https://app.neostore.cloud","description":"Production Server"},{"url":"https://app-qa.neostore.cloud","description":"Staging Server"}],"security":[{"admin-bearer":["tenant.generalSecret:read"]},{"apiKey":[]}],"components":{"securitySchemes":{"admin-bearer":{"type":"oauth2","flows":{"implicit":{"authorizationUrl":"https://auth.neostore.cloud/authorize?audience=https://app.neostore.cloud/api/","scopes":{}}}},"apiKey":{"type":"apiKey","name":"X-API-KEY","in":"header"}},"schemas":{"GeneralSecretsDefinition":{"type":"object","properties":{"secret1":{"type":["null","string"],"description":"First secret used for signing. Null when the tenant has none configured yet."},"secret2":{"type":["null","string"],"description":"Second secret used for signing. Null when the tenant has none configured yet."}},"additionalProperties":false,"description":"General secrets of the tenant."},"ProblemDetails":{"type":"object","properties":{"type":{"type":["null","string"]},"title":{"type":["null","string"]},"status":{"type":["null","integer"],"format":"int32"},"detail":{"type":["null","string"]},"instance":{"type":["null","string"]}},"additionalProperties":{}},"HttpValidationProblemDetails":{"type":"object","allOf":[{"$ref":"#/components/schemas/ProblemDetails"}],"properties":{"errors":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}}},"additionalProperties":{}}}},"paths":{"/api/{tenantId}/admin/security/secrets":{"get":{"tags":["Security"],"summary":"Get the general secrets","description":"Returns `secret1` and `secret2` of `security.yml` in clear — they are the values an\nintegrator signs `externalIdentifier` HMACs with, so there is nothing useful to return redacted.","parameters":[{"name":"tenantId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"General secrets returned.","content":{"text/plain":{"schema":{"description":"General secrets of the tenant.","$ref":"#/components/schemas/GeneralSecretsDefinition"}},"application/json":{"schema":{"description":"General secrets of the tenant.","$ref":"#/components/schemas/GeneralSecretsDefinition"}},"text/json":{"schema":{"description":"General secrets of the tenant.","$ref":"#/components/schemas/GeneralSecretsDefinition"}}}},"404":{"description":"The tenant has no readable `security.yml`.","content":{"text/plain":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}},"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}},"text/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}}}}}}}}}
```

## Configure the general secrets

> Replaces \`secret1\` and \`secret2\` of \`security.yml\`. Every other section of the file,\
> the check-in policy included, is written back unchanged.\
> &#x20;           \
> \*\*Rotating a secret invalidates every link already signed with it.\*\* Both values are replaced on\
> every call, so send back the current value of the one you do not mean to change.

```json
{"openapi":"3.1.1","info":{"title":"Neostore internal API","version":"v1"},"tags":[{"name":"Security"}],"servers":[{"url":"https://app.neostore.cloud","description":"Production Server"},{"url":"https://app-qa.neostore.cloud","description":"Staging Server"}],"security":[{"admin-bearer":["tenant.generalSecret:write"]},{"apiKey":[]}],"components":{"securitySchemes":{"admin-bearer":{"type":"oauth2","flows":{"implicit":{"authorizationUrl":"https://auth.neostore.cloud/authorize?audience=https://app.neostore.cloud/api/","scopes":{}}}},"apiKey":{"type":"apiKey","name":"X-API-KEY","in":"header"}},"schemas":{"UpdateGeneralSecretsRequest":{"required":["secret1","secret2"],"type":"object","properties":{"secret1":{"minLength":1,"type":"string","description":"First secret used for signing."},"secret2":{"minLength":1,"type":"string","description":"Second secret used for signing."}},"additionalProperties":false,"description":"Request body to configure the general secrets. Both values are replaced."},"GeneralSecretsDefinition":{"type":"object","properties":{"secret1":{"type":["null","string"],"description":"First secret used for signing. Null when the tenant has none configured yet."},"secret2":{"type":["null","string"],"description":"Second secret used for signing. Null when the tenant has none configured yet."}},"additionalProperties":false,"description":"General secrets of the tenant."},"ProblemDetails":{"type":"object","properties":{"type":{"type":["null","string"]},"title":{"type":["null","string"]},"status":{"type":["null","integer"],"format":"int32"},"detail":{"type":["null","string"]},"instance":{"type":["null","string"]}},"additionalProperties":{}},"HttpValidationProblemDetails":{"type":"object","allOf":[{"$ref":"#/components/schemas/ProblemDetails"}],"properties":{"errors":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}}},"additionalProperties":{}}}},"paths":{"/api/{tenantId}/admin/security/secrets":{"put":{"tags":["Security"],"summary":"Configure the general secrets","description":"Replaces `secret1` and `secret2` of `security.yml`. Every other section of the file,\nthe check-in policy included, is written back unchanged.\n            \n**Rotating a secret invalidates every link already signed with it.** Both values are replaced on\nevery call, so send back the current value of the one you do not mean to change.","parameters":[{"name":"tenantId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"description":"Request body to configure the general secrets. Both values are replaced.","$ref":"#/components/schemas/UpdateGeneralSecretsRequest"}},"text/json":{"schema":{"description":"Request body to configure the general secrets. Both values are replaced.","$ref":"#/components/schemas/UpdateGeneralSecretsRequest"}},"application/*+json":{"schema":{"description":"Request body to configure the general secrets. Both values are replaced.","$ref":"#/components/schemas/UpdateGeneralSecretsRequest"}}},"required":true},"responses":{"200":{"description":"General secrets updated.","content":{"text/plain":{"schema":{"description":"General secrets of the tenant.","$ref":"#/components/schemas/GeneralSecretsDefinition"}},"application/json":{"schema":{"description":"General secrets of the tenant.","$ref":"#/components/schemas/GeneralSecretsDefinition"}},"text/json":{"schema":{"description":"General secrets of the tenant.","$ref":"#/components/schemas/GeneralSecretsDefinition"}}}},"400":{"description":"One of the secrets is empty.","content":{"text/plain":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}},"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}},"text/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}}}},"404":{"description":"The tenant has no readable `security.yml`.","content":{"text/plain":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}},"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}},"text/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}}}}}}}}}
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.thewalletcrew.io/api-reference/tenant-configuration/security.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
