Skip to content

feat(intercom): intercom resolver for contact profile and conversation metadata#142

Open
jaylann wants to merge 1 commit into
stagefrom
feat/intercom-resolver
Open

feat(intercom): intercom resolver for contact profile and conversation metadata#142
jaylann wants to merge 1 commit into
stagefrom
feat/intercom-resolver

Conversation

@jaylann

@jaylann jaylann commented Jun 14, 2026

Copy link
Copy Markdown
Owner

Summary

  • New first-party package effaced-intercom (IntercomResolver) — exports a data subject's Intercom contact profile + conversation metadata (Art. 15) and erases the contact (Art. 17), mirroring the effaced-resend shape (httpx, fresh client per call, faked at the transport boundary, AttestingResolver, no rectify).
  • Refs are kind="intercom", value=<Intercom internal contact id>; get/delete address the contact by id directly (no email search). Conversation metadata is paged through POST /conversations/search filtered by contact_ids.
  • Wires the package into ruff/mypy/coverage, the generated API reference, PROOFS.md, and flips the roadmap entry to shipped.

Closes #61

Erasure/export semantics

New surface, additive — not a change to what gets deleted or exported for any existing resolver, so MINOR (not breaking).

  • Export (Art. 15): contact email/phone (CONTACT), name (IDENTITY); per-conversation created_at/updated_at/state (BEHAVIORAL), keyed conversation.{id}.{field}. Never exported: message bodies (conversation_parts, source.body) and the caller-defined custom_attributes blob — declared as covered-surface exclusions.
  • Erase (Art. 17): DELETE /contacts/{id}. A 404 on get/delete is success (already_absent=True); idempotent by construction. Erasure deletes the contact only — conversation retention is Intercom's, documented in the README and the covered-surface notes.
  • The exported field set is behaviour under widened SemVer going forward: removing/recategorising a field later is MAJOR.

Risk

  • Low: net-new package, zero change to existing code paths or exports. Pure-additive wiring entries.
  • The exact Intercom permanent-deletion semantics (DELETE /contacts/{id} archives vs. hard-deletes) and the conversation-search contact_ids field are pinned against current Intercom docs; the resolver is robust to either via the 404-idempotency contract and records what it did in ResolverErasure.detail.
  • Not added to release-please-config.json/manifest yet — matching the existing effaced-django precedent on stage (release wiring added deliberately when publishing is intended). Easy follow-up when this package is slated to publish.

Checklist

  • Targets stage (never main)
  • PR title is Conventional Commits (type(scope)?: lowercase subject)
  • All commits DCO signed-off (git commit -s)
  • just check and just test green locally
  • Tests added/updated — erasure/export paths prove no cross-subject bleed (test_intercom_properties.py)
  • Public API has Google-style docstrings
  • Manifest format untouched, or schema version bumped + migration added
  • At least one type:* and relevant area:* labels set

…n metadata

Adds effaced-intercom, a first-party httpx resolver mirroring the
effaced-resend shape (fresh client per call, sync wrapped in
asyncio.to_thread, faked at the transport boundary in tests).

- Refs are kind="intercom", value=<Intercom internal contact id>;
  get/delete address the contact by id directly (no email search).
- Export (Art. 15): contact email/name/phone plus per-conversation
  created_at/updated_at/state, paged through POST /conversations/search
  filtered by contact_ids. Message bodies and the caller-defined
  custom_attributes blob are never exported.
- Erase (Art. 17): DELETE /contacts/{id}; a 404 on get/delete is success
  (already_absent=True). Idempotent by construction.
- AttestingResolver: covered_surface built from the exporter's field
  tuples so declaration and implementation cannot drift.
- Error taxonomy is status-code-only: 4xx except 404/429 raise
  ResolverError (carrying neither id nor token); 429/5xx/connection
  faults propagate for saga retry. No rectify_subject in this first cut.

Additive (new package, no change to existing exports) — MINOR. Wires the
package into the ruff/mypy/coverage lists, the generated API reference,
PROOFS.md, and flips the roadmap entry to shipped.

Closes #61

Signed-off-by: Justin Lanfermann <Justin@Lanfermann.dev>
@jaylann jaylann added type:feat New capability area:resolvers Resolver interface & registry labels Jun 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:resolvers Resolver interface & registry type:feat New capability

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: intercom resolver

1 participant