---
title: "Get My Fulfillment"
method: GET
path: "/me/fulfillments/{id}"
tags: ["fulfillments"]
---

# Get My Fulfillment

`GET /me/fulfillments/{id}`

## Request

- Base URL: `https://api.instruxi.dev/api/v1/enforcer`
- URL: `https://api.instruxi.dev/api/v1/enforcer/me/fulfillments/{id}`
- Auth: API key in header `Authorization`

## Path parameters

- `id` string, required

## Response `200`

OK

- InternalDeliveryHttpHandlerFulfillmentResponse
  - `data` EnforcerV3InternalDomainFulfillmentFulfillment
    - `block_number` integer
    - `chain_id` integer
    - `claimed_at` string
    - `created_at` string
    - `created_by_user_id` string
    - `deadline` string
    - `declined_at` string
    - `declined_by_user_id` string
    - `deleted_at` string
    - `delivery_legs` EnforcerV3InternalDomainFulfillmentDeliveryLeg[]: DeliveryLegs is what the RECEIVER got, which no on-chain event reports — see migration 000077. Neither Routed nor GPVRecorded carries an output token or amount, and the Router's per-leg distribution emits nothing, so this is resolved from Book.getPlan plus the receipt's Transfer logs. PlanVerified gates it: only TRUE means our re-hash matched the event's planHash and the legs are exact. FALSE means inferred (the plan changed under us, or an unrecoverable inlinePlan) and NULL means never attempted. Neither is "probably fine" — 25.6% of settled rows on prod were paid under a plan that has since changed, because a receiver can rewrite their own plan at will (Book.setPlan is ungated on msg.sender).
      - `bps` integer
      - `delivered` string: Delivered is base units of Token, read from the receipt's Transfer logs (the chain publishes it nowhere else). Empty when not resolved.
      - `token` string
      - `wallet` string
    - `expected_payer_wallet` string: ExpectedPayerWallet, when set, is the ONLY wallet that may fund this fulfillment — see PayerBound and migration 000101. Never inferred from an account and never backfilled from whoever paid; sender_wallet is the snapshot/observed field and this is the RULE.
    - `expected_plan_hash` string
    - `expired_at` string
    - `fee_charged` string
    - `id` string
    - `idempotency_key` string: IdempotencyKey de-duplicates CREATE (migration 000082). Optional: when a caller supplies one, a second create with the same (tenant_id, key) returns the ORIGINAL row instead of minting a second payment. Unique per tenant over non-NULL values, for the row's whole lifetime — a retry that arrives after the original settled must still resolve to it, which is exactly the "answer lost, client retried later" case.
    - `input_spent_actual` string
    - `input_token_actual` string
    - `kind` string: Kind is the product line this fulfillment belongs to: p2p | deposit | billpay | direct | reward. Derived server-side from the reference tag at create (ClassifyKind) and stored, rather than re-derived per query from a free-text column. Empty only on rows written before migration 000075 or by a pod still on the previous image; the query layer falls back to the reference for those.
    - `max_fee_bps` integer
    - `memo` string: bytes32 on-chain memo — the join key between this record and chain state.
    - `notional_actual` string
    - `notional_uoa` string
    - `paid_uoa` string: PaidUOA is the summed ROUTED settlements for this row's memo — what actually arrived, against NotionalUOA's what-was-owed. The excess is PaidUOA - NotionalUOA, and it is the reason this field exists: the overpaid filter finds rows where a payer routed the same memo twice or a route declared more than the request asked for, but a boolean cannot tell support HOW MUCH. DERIVED, not a column (gorm:"-"): computed per page by the admin read paths. A POINTER because absent must stay distinguishable from zero — on any endpoint that does not compute it, a plain string would render "0", which reads as "nothing ever arrived" for a fully paid transfer.
    - `payer_address` string
    - `plan_hash_actual` string
    - `plan_verified` boolean
    - `recall_reason` string
    - `recalled_at` string
    - `recalled_by_user_id` string
    - `receiver` EnforcerV3InternalDomainFulfillmentUserDisplay
      - `email` string
      - `email_verified` boolean
      - `first_name` string
      - `is_contact` boolean: IsContact is true when this counterparty is already a saved (linked) contact of the caller viewing the row, so the PWA can badge it without a second lookup. Computed per-viewer; false for the caller themselves.
      - `last_name` string
      - `phone_number` string
      - `phone_number_verified` boolean
      - `user_id` string
      - `username` string
      - `wallet_address` string: WalletAddress is the counterparty's on-file wallet (from their account), so the row renders the send/receive wallet alongside the name.
    - `receiver_paid_ack_at` string
    - `receiver_seen_at` string
    - `receiver_user_id` string
    - `receiver_wallet` string
    - `reference` string
    - `refund_wallet` string: RefundWallet, when set, is where a refund of this sale is sent instead of the wallet that paid it (migration 000116). Set only at create, by a service caller; exposed on reads beside receiver_wallet so the destination can be shown.
    - `refunded_uoa` string: RefundedUOA is how much has actually been refunded AGAINST this payment: the summed routed settlements of every refund naming it. Same shape and the same reason as PaidUOA — a boolean "refunded" cannot answer "how much", and partial refunds are the normal case. A POINTER because absent must stay distinguishable from zero: on an endpoint that does not compute it, a plain string would render "0", which reads as "never refunded" for a fully refunded payment.
    - `refunds_fulfillment_id` string: RefundsFulfillmentID is the payment this one REVERSES, parsed from the reference. Derived, not a column: the reference is the record, and a second stored copy could disagree with it.
    - `request_token` string
    - `sender` EnforcerV3InternalDomainFulfillmentUserDisplay
      - `email` string
      - `email_verified` boolean
      - `first_name` string
      - `is_contact` boolean: IsContact is true when this counterparty is already a saved (linked) contact of the caller viewing the row, so the PWA can badge it without a second lookup. Computed per-viewer; false for the caller themselves.
      - `last_name` string
      - `phone_number` string
      - `phone_number_verified` boolean
      - `user_id` string
      - `username` string
      - `wallet_address` string: WalletAddress is the counterparty's on-file wallet (from their account), so the row renders the send/receive wallet alongside the name.
    - `sender_cost_uoa` string: SenderCostUOA is value_at_mid(input_spent_actual) - notional_uoa: what the route cost the sender ABOVE what was delivered. fee_charged is only part of it — on the Aug-26 route the fee was 2.99 USDC and the true cost 13.18. SlippageUOA is the residual after the fee. ★ It is NOT always "lost": on a DIRECT same-token leg the principal never swaps, so the gap is a transfer to the RECEIVER rather than a loss to liquidity providers. Check DeliveryLegs before calling it a loss. Negative means the route beat mid, and it is stored signed so a better-than-mid execution is not clamped away. NULL means NOT COMPUTED — no pool for the input token, an RPC failure, or a row predating migration 000081. It never means zero.
    - `sender_seen_at` string
    - `sender_user_id` string
    - `sender_wallet` string
    - `settled_at` string
    - `slippage_uoa` string
    - `status` string
    - `submitted_at` string
    - `tenant_id` string
    - `tx_hash` string
    - `updated_at` string
  - `success` boolean

## Other responses

- `401`: Unauthorized
- `403`: Forbidden
- `404`: Not Found

---

[API](https://skmtc.dev/instruxi/apis/enforcer-api.md) · [All operations](https://skmtc.dev/instruxi/apis/enforcer-api/llms.txt) · [OpenAPI document](https://skmtc.dev/instruxi/apis/enforcer-api/revisions/fa82bc1becc0?raw)
