{"lexicon":1,"id":"money.atmosphere.membership.registerGrant","defs":{"main":{"type":"procedure","description":"Register a non-payment membership grant (comp, migration import, or free-tier claim) so cross-app access checks honor it: money.atmosphere.membership.getAccess answers from the union of attested subscription truth and registered grants. Requires registered-app service-auth, and the calling app must hold creator consent: an approved recipient approval covering subscription payments (or be the creator itself). Consent is re-asserted when grants are read for access, so revoking or narrowing an app's approval immediately stops its grants from conferring access. Grants are PRIVATE ATM ledger state — never public records and never a queryable member list; they carry DIDs only, and the reference field must not contain emails or other PII. Re-registering the same (creator, member, program/tier, source) tuple updates the existing grant rather than duplicating it.","input":{"encoding":"application/json","schema":{"type":"object","required":["memberDid","creatorDid","source"],"properties":{"memberDid":{"type":"string","format":"did","description":"Member DID receiving the grant."},"creatorDid":{"type":"string","format":"did","description":"Creator or project DID whose membership is being granted."},"program":{"type":"string","format":"at-uri","description":"Optional money.atmosphere.membership.program AT-URI in the creator's repo."},"tier":{"type":"string","format":"at-uri","description":"Optional money.atmosphere.membership.tier AT-URI in the creator's repo. When absent, the grant is program-level."},"source":{"type":"string","knownValues":["comp","migration","free_claim"],"maxLength":32,"description":"Why the grant exists: a creator comp, a billing-migration import, or a free-tier claim flow."},"environment":{"type":"string","knownValues":["test","live"],"maxLength":8,"description":"ATM app environment. Defaults to live."},"expiresAt":{"type":"string","format":"datetime","description":"Optional expiry after which the grant no longer satisfies access checks. Absent means open-ended until revoked."},"reference":{"type":"string","maxLength":256,"description":"Optional opaque app-local reference for reconciliation (for example a migration batch or comp id). Must not contain emails or other PII."}}}},"output":{"encoding":"application/json","schema":{"type":"object","required":["grantId","memberDid","creatorDid","registeredAt"],"properties":{"grantId":{"type":"string","maxLength":128,"description":"Opaque grant id for later revocation."},"memberDid":{"type":"string","format":"did"},"creatorDid":{"type":"string","format":"did"},"registeredAt":{"type":"string","format":"datetime","description":"When ATM recorded the grant."},"expiresAt":{"type":"string","format":"datetime","description":"Echoed expiry, when set."}}}},"errors":[{"name":"InvalidActor","description":"Member or creator DID is malformed."},{"name":"AppNotRegistered","description":"Caller is not a registered ATM app."},{"name":"NotAuthorized","description":"Calling app is not authorized to grant memberships for this creator."},{"name":"InvalidTarget","description":"Program or tier AT-URI is malformed or is not a membership record in the creator's repo."}]}}}