API-ключи
`keys.list`, `list_all`, `iterate`, `get`, `create`, `update`, `delete`, `rotate` и `revoke`, а также чтение журнала запросов и активности.
Все методы
from pathlib import Path from openemail import openemail key = openemail.keys.create({ 'name': 'Billing sender', 'scopes': ['emails:send'], 'domainAllowlist': ['billing.acme.com'], 'expiresInMinutes': 60 * 24 * 90,}) secret = Path('.openemail-billing-key')secret.touch(mode=0o600)secret.write_text(key['token']) openemail.keys.update(key['id'], {'enabled': False}) rotated = openemail.keys.rotate(key['id'])secret.write_text(rotated['token']) openemail.keys.revoke(key['id'], {'reason': 'Replaced'})openemail.keys.delete(key['id'])create и rotate единственные вызовы, которые возвращают секрет, в token, один раз. Любое чтение вместо него возвращает maskedKey. update выключает и включает ключ через enabled, это обратимая альтернатива revoke, а delete убирает только уже отозванный ключ. Чтение требует keys:read, любое изменение keys:manage.
Клиент никогда не повторяет create или rotate. Повтор после потерянного ответа выпустил бы второй ключ или обесценил бы секрет, который вернула первая попытка. update и revoke повторяются как чтения, потому что их повтор оставляет тот же ключ, а delete не повторяется.
Никогда не шире вызывающего
Ключ никогда не создаёт и не достаёт ключ шире себя: скоупы, роль, срок действия, режим и область отправки должны лежать внутри вызывающего ключа, иначе вызов отклоняется с 403 beyond_caller_authority. Ключ, суженный до некоторых доменов или адресов, видит только ключи внутри своей области отправки. rotate для самого вызывающего ключа работает и с keys:write, как me.rotate().
Повторное подтверждение нельзя применить к вызову, сделанному ключом, поэтому keys:manage это доступ, который создаёт доступы. Давайте его только автоматизации, выпускающей ключи, дайте этому ключу роль, область отправки и срок действия и следите за list_workspace_activity, где всё, что он делает, записано на него.
Журнал запросов и активность
from datetime import datetime, timedelta, timezone from openemail import openemail failures = openemail.keys.list_requests( '4c1b257a66287fd113bd89d0', failed_only=True, since=datetime.now(timezone.utc) - timedelta(days=1),)for request in failures['items']: print(request['status'], request['method'], request['path']) for change in openemail.keys.iterate_workspace_activity(): actor = change['actor'] print(change['keyName'], change['type'], actor['label'] if actor else None)list_requests и list_activity читают один ключ, list_workspace_requests и list_workspace_activity читают все ключи или те, что названы в key_ids, и у каждого рядом есть list_all_… и iterate_…. Они принимают since и until, а методы журнала запросов ещё и failed_only.
since и until принимают datetime или строку ISO 8601. datetime отправляется в UTC, а наивный сначала читается как местное время.
Справочник
keys.list()Полный справочникkeys.list_all()Полный справочникkeys.iterate()Полный справочникkeys.get()Полный справочникkeys.create()Полный справочникkeys.update()Полный справочникkeys.delete()Полный справочникkeys.rotate()Полный справочникkeys.revoke()Полный справочникkeys.list_requests()Полный справочникkeys.list_all_requests()Полный справочникkeys.iterate_requests()Полный справочникkeys.list_activity()Полный справочникkeys.list_all_activity()Полный справочникkeys.iterate_activity()Полный справочникkeys.list_workspace_requests()Полный справочникkeys.list_all_workspace_requests()Полный справочникkeys.iterate_workspace_requests()Полный справочникkeys.list_workspace_activity()Полный справочникkeys.list_all_workspace_activity()Полный справочникkeys.iterate_workspace_activity()Полный справочник