async def METHOD_NAME(
    method, path, data=None, text=False, params=None, headers=None, tenant=False
) -> ClientResponse:
    """
    Generic routine to call an Aca-Py admin api.
    Default headers are used if not supplied, and security headers are injected.
    Args:
        method: http method (i.e. GET, POST, etc)
        path: endpoint path (e.g. /multitenancy/wallet)
        data: json object to POST (default None)
        text: expect a plaintext response (default False which means json response)
        params: dict of query params to include in request
        headers: dict of headers to include in the request (default None)
        tenant: whether request is in context of a tenant (default False)
    Returns:
        aiohttp response object
    """
    params = {k: v for (k, v) in (params or {}).items() if v is not None}
    url = f"{settings.ACAPY_ADMIN_URL}/{path}"
    # TODO where should this live, and how do we want to handle HTTP sessions?
    async with ClientSession() as client_session:
        headers = get_acapy_headers(headers, tenant)
        async with client_session.request(
            method,
            url,
            json=data,
            params=params,
            headers=headers,
        ) as resp:
            resp_text = await resp.text()
            try:
                resp.raise_for_status()
            except Exception as e:
                # try to retrieve and print text on error
                raise Exception(f"Error: {resp_text}") from e
            if not resp_text and not text:
                return None
            if not text:
                try:
                    return json.loads(resp_text)
                except json.JSONDecodeError as e:
                    raise Exception(f"Error decoding JSON: {resp_text}") from e
            return resp_text