{% extends "base.html" %} {% block title %}{{ t('nav.api') }} — painfree{% endblock %} {% block body %}

{{ t('api.heading') }}

{{ t('api.lede') }}

{# This page is about the machine-facing side of the service, and that side is English by decision: an endpoint path, a scope name, an error `code` and an audit `action` are contract values a consumer matches on, so translating one would break every client. What is translated here is the prose around them. What is not is said so on the page rather than left to look like a hole in the catalogue. #}

{{ t('api.english_notice') }}

{{ t('api.schema') }}

{% for path, name, blurb in documents %} {% endfor %}
{{ name }} {{ blurb }}
{{ base_url }}{{ path }}

{{ t('api.schema_note') }}

{{ t('api.authentication') }}

{% if auth_mode == 'basic' %}

{{ t('api.basic_credential') }} {{ t('api.basic_why') }}

{{ t('api.basic_get_one') }}

curl -u "$PAINFREE_ACCOUNT:$PAINFREE_PASSWORD" \
     -H "Idempotency-Key: $(uuidgen)" \
     -X POST {{ base_url }}/v1/connections/<connection>/payments \
     -d @payment.json

{{ t('api.basic_tls') }}

{{ t('api.basic_throttle') }}

{% else %}

{{ t('api.no_tokens') }} {{ t('api.no_tokens_why') }}

{{ t('api.get_a_token') }}

curl -H "Authorization: Bearer $TOKEN" \
     -H "Idempotency-Key: $(uuidgen)" \
     -X POST {{ base_url }}/v1/connections/<connection>/payments \
     -d @payment.json
{% endif %} {% if auth_mode == 'basic' %} {% else %} {% endif %}
{{ t('api.mode') }}{{ auth_mode }}
{{ auth_mode_reason }} {% if auth_mode == 'development' %}
{{ t('api.development_mode') }} {% endif %} {% if auth_mode == 'basic' %}
{{ t('api.basic_mode') }} {% endif %}
{{ t('api.basic_scheme') }}Basic realm="painfree"
{{ t('api.basic_hash') }}{{ password_hashing }}
{{ t('api.basic_lockout') }}{{ t('api.basic_lockout_value') }}
{{ t('api.issuer') }}{{ provider.issuer or t('api.not_configured') }}
{{ t('api.audience') }}{{ provider.audience or '—' }}
{{ t('api.client_id') }}{{ provider.client_id or '—' }}
{{ t('api.roles_claim') }}{{ provider.roles_claim }}
{{ t('api.scope_claim') }}{{ provider.scope_claim }}
{% if auth_mode == 'basic' %}

{{ t('api.basic_sign_out') }}

{% else %}

{{ t('api.roles_note') }}

{% endif %}

{{ t('api.you') }}

{{ t('access.subject') }}{{ me.subject }}
{{ t('api.issuer') }}{{ me.issuer }}
{{ t('api.authenticated_by') }}{{ me.method }}
{{ t('api.role') }}{{ me.role }} {% if me.role == 'admin' %}
{{ t('api.role_admin') }} {% else %}
{{ t('api.role_member') }} {% endif %}
{{ t('access.access') }} {% if me.role == 'admin' %} {{ t('api.every_connection') }} {% elif me.oversight and not me.grants %} oversight {{ t('access.every_connection_readonly') }} {% elif me.grants %} {% if me.oversight %}oversight {{ t('access.every_connection_readonly') }}
{% endif %} {% for grant in me.grants %} {{ grant.connection_id }} {{ grant.level }}{{ '' if loop.last else ' · ' }} {% endfor %} {% else %} {{ t('api.no_grants') }} {% endif %}
{{ t('api.claimed_roles') }}{{ me.roles | join(', ') or t('access.none') }} {% if unmapped %} {# A provider that starts sending a role name this service has no mapping for grants nothing, silently. Saying so here is the difference between a five-minute fix and an afternoon. #}
{{ t('api.unmapped') }} {% for name in unmapped %}{{ name }}{{ '' if loop.last else ', ' }}{% endfor %} {% endif %}
{{ t('api.expires') }}{{ me.expires_at or '—' }}
{{ t('api.effective_scopes') }} {% if me.scopes %} {% for name in me.scopes %}{{ name }}{{ '' if loop.last else ' · ' }}{% endfor %} {% if me.role != 'admin' and not me.oversight %}
{{ t('api.scopes_per_connection') }} {% elif me.oversight %}
{{ t('api.scopes_oversight') }} {% endif %} {% else %} {{ t('api.scopes_none') }} {% endif %}

{{ t('api.auth_me') }} GET /auth/me{{ t('api.auth_me_tail') }}

{{ t('api.privileges') }}

{{ t('api.privileges_lede') }}

{% for row in roles %}{% endfor %} {% for row in grant_levels %}{% endfor %} {% for scope in scopes %} {% for row in roles %} {% endfor %} {% for row in grant_levels %} {% endfor %} {% endfor %}
{{ t('api.scope') }}{{ t('api.what_it_allows') }}{{ t('api.role_column') }}: {{ row.role }}{{ t('api.level_column') }}: {{ row.level }}oversight {{ t('api.you_column') }}
{{ scope.scope }} {{ scope.summary }} {% if scope.admin_only %}
{{ t('api.admin_only') }} {% endif %}
{{ '✓' if scope.scope in row.scopes else '—' }}{{ '✓' if scope.scope in row.scopes else '—' }}{{ '✓' if scope.oversight else '—' }} {{ '✓' if scope.held else '—' }}

{{ t('api.oversight_carries') }} {% for name in oversight_scopes %}{{ name }}{{ '.' if loop.last else ' · ' }}{% endfor %} {{ t('api.oversight_derived') }}

{{ t('api.endpoints') }}

{{ t('api.endpoints_lede') }}

{% for row in routes %} {% endfor %}
{{ t('api.method') }}{{ t('api.path') }} {{ t('api.what') }}{{ t('api.needs') }}
{{ row.method }} {{ row.path }} {{ row.summary }} {% if row.role %} {{ row.role }} {{ t('api.only') }}
{{ t('api.a_role_not_a_scope') }} {% elif row.scopes %} {% for name in row.scopes %}{{ name }}{{ '' if loop.last else ' + ' }}{% endfor %} {% if row.per_connection %}
{{ t('api.on_this_connection') }} {% endif %} {% elif row.public %} {{ t('api.no_credential') }} {% else %} {{ t('api.any_authenticated') }} {% endif %}

{{ t('api.any_credential') }}

{% if unscoped %}

{{ t('api.any_credential_why') }}

{% else %}

{{ t('api.any_credential_none') }}

{% endif %}

{{ t('api.public_paths') }}

{% endblock %}