nearid-sdk 2.0.0
NearID Python SDK
Requires Python: >=3.9
Keywords: nearid, presence, ble, security, sdk

# NearID Python SDK

This directory contains the official Python SDK for interacting with the NearID Cloud backend.
It provides a simple, clean API client plus webhook verification utilities.

All SDK behavior follows: protocol/spec.md

---

## Features

- REST API client (requests-based)
- Webhook signature verification
- Typed dataclasses for events, links, sessions
- Error handling and pagination helpers
- Full API reference (method-by-method)

---

## Installation

pip install nearid-sdk

---

## API Reference

See the complete method-by-method docs, parameters, responses, and errors in:

- `../API_REFERENCE.md`

Requires: Python 3.9+ and `requests`.

---

## Usage Example

from nearid_sdk import NearIDClient

client = NearIDClient(
    api_key="YOUR_API_KEY_OR_ORG_TOKEN",
    org_id="org_123",
    base_url="https://api.nearid.example",
)

events = client.list_events()

client.link_presence_session(
    presence_session_id="psess_001",
    user_ref="emp_45"
)

---

## Webhook Verification

from nearid_sdk import verify_webhook_signature

is_valid = verify_webhook_signature(
    secret=webhook_secret,
    timestamp=timestamp_header,
    raw_body=raw_body_bytes,
    signature_hex=signature_header,
)

---

## Flask Webhook Example

```python
import os
from flask import Flask, request, jsonify
from nearid_sdk import verify_webhook_signature

app = Flask(__name__)

WEBHOOK_SECRET = os.environ["WEBHOOK_SECRET"]

@app.route("/nearid/webhook", methods=["POST"])
def nearid_webhook() -> "tuple[dict, int]" | "tuple[dict, int, dict]":
    signature = request.headers.get("X-HNNP-Signature", "")
    timestamp = request.headers.get("X-HNNP-Timestamp", "")
    raw_body = request.get_data()  # bytes

    if not verify_webhook_signature(WEBHOOK_SECRET, timestamp, raw_body, signature):
        return jsonify({"error": "invalid_webhook_signature"}), 400

    # At this point, request.json is trusted NearID payload
    print("Received NearID webhook:", request.json)
    return jsonify({"status": "ok"}), 200
```

---

## FastAPI Webhook Example

```python
import os
from fastapi import FastAPI, Request, HTTPException
from nearid_sdk import verify_webhook_signature

app = FastAPI()
WEBHOOK_SECRET = os.environ["WEBHOOK_SECRET"]

@app.post("/nearid/webhook")
async def nearid_webhook(request: Request):
    signature = request.headers.get("X-HNNP-Signature", "")
    timestamp = request.headers.get("X-HNNP-Timestamp", "")
    raw_body = await request.body()  # bytes

    if not verify_webhook_signature(WEBHOOK_SECRET, timestamp, raw_body, signature):
        raise HTTPException(status_code=400, detail="invalid_webhook_signature")

    payload = await request.json()
    print("Received NearID webhook:", payload)
    return {"status": "ok"}
```

---

## Methods

client.list_receivers()
client.create_receiver(...)
client.update_receiver(...)
client.list_events()
client.list_sessions()
client.list_links()
client.create_link(...)
client.create_link_v2(...)
client.link_presence_session(presence_session_id, user_ref)
client.activate_link(link_id)
client.revoke_link(link_id)
client.revoke_link_v2(link_id, org_id)
client.unlink(link_id)  # alias for revoke_link

# Extended org console APIs (requires org admin auth)
client.list_orgs(...)
client.get_org(org_id)
client.get_org_settings(org_id)
client.update_org_settings(org_id, settings)
client.get_org_config(org_id)
client.update_org_config(org_id, config)
client.get_org_metrics_realtime(org_id, params)
client.list_org_users(org_id)

client.list_org_sessions(...)
client.get_org_sessions_summary(...)
client.create_org_session(payload)
client.update_org_session(session_id, payload)
client.delete_org_session(session_id)
client.finalize_org_session(session_id)

client.get_presence_live(...)
client.get_presence_logs(...)
client.get_presence_events(...)
client.list_locations()
client.list_groups()
client.create_group(payload)
client.update_group(group_id, payload)
client.delete_group(group_id)
client.get_attendance(...)
client.export_attendance_csv(...)

client.get_billing_summary()
client.list_invoices()
client.get_invoice_pdf(invoice_id)

client.list_incidents(...)
client.get_incident(incident_id)
client.get_incident_rollcall(incident_id)
client.get_safety_status()
client.get_safety_summary()

---

## Typed models

Dataclasses are available in `nearid_sdk.types` for receivers, events, sessions, billing, incidents, and safety summaries.
Each model includes a `.raw` field with the original JSON payload if you need it.

Webhook event payloads are also typed:
- WebhookEvent
- WebhookPresenceCheckIn
- WebhookPresenceUnknown
- WebhookLinkCreated
- WebhookLinkRevoked

---

## File Structure

nearid_sdk/
  client.py
  http.py
  webhook.py
  types.py
  __init__.py

tests/
  test_client.py
  test_webhook.py

---

## Environment Variables (optional)

WEBHOOK_SECRET=your_org_webhook_secret

---

## Notes

- HMAC verification MUST use exact rules defined in Section 10.3 of spec.md (v2).
- All network calls MUST use HTTPS
- Must not alter any packet format or cryptographic behavior
- Some endpoints require an **org admin JWT** (org console auth) instead of an API key.
- Presence events include `verification_status` with values `verified`, `weak`, or `rejected`.
- If `verification_status` is `verified` and `user_ref` is present, the event is linked.
- Receiver `auth_mode` values are `hmac_shared_secret` or `public_key`.

---

## Pagination helpers

```python
for evt in client.iterate_events(org_id="org_123"):
    print(evt.id)

for sess in client.iterate_sessions(org_id="org_123"):
    print(sess.session_id)

for rcv in client.iterate_receivers(org_id="org_123"):
    print(rcv.receiver_id)
```

---

## Rate limiting

The client retries on `5xx` and `429` responses with exponential backoff.  
If `Retry-After` is provided, it is respected (up to 30s).

---

## Testing

python -m pytest -q

---

## Reference

Full protocol: ../../protocol/spec.md