Skip to content

The monthly renewal cycle

Most integrations settle into the same cadence: once a month, pull the renewal events falling due two to three months out, decide each one, and send the instructions in a batch. The timing is driven by the instruction window: an event becomes instructable on opensAt, the first day of the month two months before its due month. Annuities due in October 2026 open on 1 August 2026, so a run at the start of each month picks up the due month that just opened, plus anything nearer that is still undecided.

This guide walks one full cycle end to end, from the event list to the reconciled invoice. It assumes you have an API key with the patents:read, patents:instructions:write and invoices:read scopes (see Getting started and Authentication). For the underlying model, see The patent lifecycle; for any unfamiliar term, the glossary.

The worked example below runs in early August 2026 and targets the October 2026 dues.

Step 1: Pull the due events

GET /patent-events is the cross-portfolio "what falls due in this window" query. Use dueMonth for a whole month (or dueFrom + dueTo for an arbitrary window), and add include=fees to get the estimated cost inline on each event:

bash
curl "https://api.renewr.example/api/v2/external/patent-events?dueMonth=2026-10&include=fees&itemsPerPage=100" \
  -H "x-api-key: $RENEWR_API_KEY"

The response is a standard list envelope. One event, abbreviated:

json
{
  "pagination": {
    "totalItems": 3,
    "currentPage": 1,
    "itemsPerPage": 100,
    "totalPages": 1,
    "hasMore": false
  },
  "data": [
    {
      "object": "patentEvent",
      "id": "3f8a1c2e-9b4d-4e6f-8a2b-1c9d7e5f3a10",
      "patent": {
        "id": "b1e6f0a2-4c8d-4b3a-9e7f-2d5c8a1b4e6d",
        "providerId": "ACME-EP-000123"
      },
      "eventType": "RENEWAL",
      "dueDate": "2026-10-31",
      "gracePeriodEndDate": "2027-04-30",
      "renewal": { "annuityYear": 5, "paymentSequenceNumber": null },
      "appliedDecision": null,
      "duePeriod": "UPCOMING",
      "processingStatus": "NOT_INSTRUCTED",
      "isFxRateFrozen": false,
      "feesStatus": "ESTIMATED",
      "instructability": {
        "isAllowed": true,
        "allowedDecisions": ["PROCEED", "SKIP_THIS_TIME", "DROP"],
        "blockedReason": null,
        "opensAt": "2026-08-01",
        "mode": "DIRECT"
      },
      "fees": {
        "object": "fees",
        "feesStatus": "ESTIMATED",
        "isFxRateFrozen": false,
        "invoiceId": null,
        "eventId": "3f8a1c2e-9b4d-4e6f-8a2b-1c9d7e5f3a10",
        "patent": {
          "id": "b1e6f0a2-4c8d-4b3a-9e7f-2d5c8a1b4e6d",
          "providerId": "ACME-EP-000123"
        },
        "currency": "EUR",
        "total": "1250.00",
        "components": [
          { "type": "OFFICE", "amount": "800.00" },
          { "type": "AGENT", "amount": "150.00" },
          { "type": "RENEWR", "amount": "300.00" }
        ]
      }
    }
  ]
}

Each event identifies its patent both ways, with the Renewr patent.id and your own patent.providerId, so you can match against your docketing system without extra lookups. If you need the full patent record, fetch it with GET /patents/{patentId}.

Page size

itemsPerPage defaults to 10 and is capped at 100 (silently clamped above that). Follow pagination.hasMore to page through larger months. See Conventions.

The fees shown here are feesStatus: "ESTIMATED": projected from the fee matrices and current exchange rates. They become INVOICED amounts later in the cycle (step 7). See Fees and FX, including how to freeze a month's exchange rates with PUT /exchange-rate-freezes/{yearMonth} for budget certainty.

Step 2: Decide, guided by instructability

Every event carries an instructability object computed by the exact same rule the instruction endpoint enforces, so what it says is what the POST will accept. The three decisions are PROCEED (pay this annuity), SKIP_THIS_TIME (handled outside Renewr this occurrence; the chain continues), and DROP (abandon the patent). See Instructions and instructability for the full semantics.

When isAllowed is false, blockedReason tells you why:

blockedReasonMeaningWhat to do
ALREADY_INSTRUCTEDThe event already has an executed decision; allowedDecisions is empty.Nothing. Read appliedDecision on the event to see what was decided.
TOO_EARLYThe instruction window has not opened yet.Wait until opensAt, then instruct.
EARLIER_EVENT_PENDINGAn older event of the same patent is still un-instructed.Instruct the older event first: only the chronologically earliest un-instructed event of a patent can be instructed.

There is one more case to handle: a lapsed event (its duePeriod is LAPSED, meaning the grace period is over and the office no longer accepts payment) stays isAllowed: true, but PROCEED disappears from allowedDecisions:

json
{
  "isAllowed": true,
  "allowedDecisions": ["SKIP_THIS_TIME", "DROP"],
  "blockedReason": null,
  "opensAt": "2026-02-01",
  "mode": "DIRECT"
}

You can only record that the annuity was handled elsewhere, or abandon the right. Posting PROCEED anyway fails with instruction_lapsed (see step 4).

Rule of thumb

Send a decision only when instructability.isAllowed is true and that decision is in allowedDecisions. Never infer instructability from duePeriod or dueDate: an event whose duePeriod still says UPCOMING can already be instructable. See Events and due periods.

DROP is terminal

Executing a DROP deletes all remaining future events of the patent, and the right will lapse at the office. There is no undo via the API. Reserve it for deliberate abandonment decisions.

Step 3: Send the instructions

Post the whole month's decisions in one batch to POST /patent-instructions:

bash
curl -X POST "https://api.renewr.example/api/v2/external/patent-instructions" \
  -H "x-api-key: $RENEWR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "instructions": [
      { "eventId": "3f8a1c2e-9b4d-4e6f-8a2b-1c9d7e5f3a10", "decision": "PROCEED" },
      { "eventId": "a94d7c31-2e5b-4f8a-b6c0-d13e9f57a2b8", "decision": "PROCEED" },
      { "eventId": "5c2e9b7d-8f1a-4d3c-a0b6-4e7f2a9c1d85", "decision": "SKIP_THIS_TIME" }
    ]
  }'

One event per patent per call

A batch can span as many different patents as you like, but only one event per patent: the chronologically earliest un-instructed one. Two events of the same patent in one batch always fail: validation runs against pre-execution state, so the second entry raises instruction_skipped and the entire batch rolls back. There are no partial writes; on any error, nothing was executed.

Instructions run in DIRECT mode: they are executed immediately and synchronously inside the POST (decision applied, receipt issued, invoice line created) and are not modifiable afterwards through the API. The 201 response is your instruction receipt:

json
{
  "object": "instructionReceipt",
  "issueDate": "2026-08-05",
  "reference": "IR-2026-0087",
  "number": 87,
  "instructions": [
    {
      "object": "instruction",
      "id": "0d9e4f2b-6a1c-4e8d-b3f5-7c2a9e0d4b16",
      "decision": "PROCEED",
      "status": "EXECUTED",
      "executedAt": "2026-08-05T09:14:27.531Z",
      "scheduledForExecutionAt": null,
      "renewedDesignatedCountries": null,
      "excludedDesignatedCountries": null
    },
    {
      "object": "instruction",
      "id": "6b3c8e1d-9f4a-4c7b-a2e5-0d8f3b6a9c21",
      "decision": "PROCEED",
      "status": "EXECUTED",
      "executedAt": "2026-08-05T09:14:27.531Z",
      "scheduledForExecutionAt": null,
      "renewedDesignatedCountries": null,
      "excludedDesignatedCountries": null
    },
    {
      "object": "instruction",
      "id": "f2a7d0c5-4e8b-4a1d-9c6f-3b5e8d0a7f42",
      "decision": "SKIP_THIS_TIME",
      "status": "EXECUTED",
      "executedAt": "2026-08-05T09:14:27.531Z",
      "scheduledForExecutionAt": null,
      "renewedDesignatedCountries": null,
      "excludedDesignatedCountries": null
    }
  ]
}

In DIRECT mode every instruction comes back status: "EXECUTED" with executedAt set. The designated-country lists are always null for patent instructions.

Step 4: Handle the failure modes

All errors are application/problem+json with a stable machine-readable code. See Errors for the full catalog. Remember from step 3: any failure rolls back the whole batch, so after fixing (or removing) the offending entry, resubmit the rest.

HTTPcodeWhenWhat to do
409 Conflictinstruction_already_setThe event already has an executed decision.Safe to skip. Drop the entry and resubmit; read appliedDecision on the event if you want to know what was decided.
422instruction_too_earlyToday is before opensAt.Hold the entry until opensAt.
422instruction_skippedAn earlier un-instructed event of the same patent must be decided first. Extensions: clientCaseReference, missingPreviousRenewalDates.Instruct the earlier event, then retry this one.
422instruction_lapsedThe decision is not offered on this event, typically PROCEED on a lapsed event.Re-read instructability.allowedDecisions and choose among them (SKIP_THIS_TIME / DROP).

A typical 409:

json
{
  "type": "/errors/instruction-already-set",
  "title": "Instruction already set",
  "status": 409,
  "detail": "This event already has an executed instruction.",
  "instance": "/api/v2/external/patent-instructions",
  "code": "instruction_already_set",
  "requestId": "7e1f4b9a-2c6d-4e8f-9a3b-5d0c7f2e8a14"
}

instruction_already_set is the normal outcome of a retry after a network timeout, or of two systems instructing the same portfolio. Treat it as confirmation that the work is already done; it is not a failure.

Step 5: Keep proof, the instruction receipt PDF

The receipt PDF is generated as part of the POST, so it is available immediately. Download it per event with GET /patents/{patentId}/events/{eventId}/documents/instruction-receipt:

bash
curl -o instruction-receipt.pdf \
  "https://api.renewr.example/api/v2/external/patents/b1e6f0a2-4c8d-4b3a-9e7f-2d5c8a1b4e6d/events/3f8a1c2e-9b4d-4e6f-8a2b-1c9d7e5f3a10/documents/instruction-receipt" \
  -H "x-api-key: $RENEWR_API_KEY"

The response is a binary application/pdf with a Content-Disposition filename (instruction-receipt-<eventId>.pdf). Archive it with the matter file; it is your proof of order. See Downloading documents for the shared mechanics of the three document endpoints.

Step 6: Track execution

After a PROCEED, Renewr pays the office and records the proof. Watch the event's processingStatus on GET /patents/{patentId}/events/{eventId} (or in bulk via GET /patent-events):

bash
curl "https://api.renewr.example/api/v2/external/patents/b1e6f0a2-4c8d-4b3a-9e7f-2d5c8a1b4e6d/events/3f8a1c2e-9b4d-4e6f-8a2b-1c9d7e5f3a10" \
  -H "x-api-key: $RENEWR_API_KEY"
json
{
  "object": "patentEvent",
  "id": "3f8a1c2e-9b4d-4e6f-8a2b-1c9d7e5f3a10",
  "appliedDecision": "PROCEED",
  "processingStatus": "PAYMENT_SENT",
  "processingStatusUpdatedAt": "2026-08-19T02:10:44.902Z"
}

The pipeline in one line: NOT_INSTRUCTED → INSTRUCTED → PAYMENT_SENT → PAYMENT_CONFIRMED → FULFILLED, with UNPROCESSABLE as the terminal problem state: the office rejected the payment or a complication occurred; contact Renewr support. See Events and due periods for the full status semantics.

When the event reaches FULFILLED, the office's official receipt is on file. Download it with GET /patents/{patentId}/events/{eventId}/documents/official-receipt (it returns 404 not_found until then). That PDF is the office's proof the annuity was actually paid.

No webhooks: poll

The v2 API has no push notifications. Poll processingStatus on your own schedule (daily is plenty; office confirmations take days to weeks). See Syncing data for the polling patterns.

Step 7: Reconcile the money

When Renewr invoices you, the fees you saw as estimates become invoiced amounts. Discover new invoices with GET /invoices, then fetch each one with GET /invoices/{invoiceId}. A single fetch returns every fee line, and each line carries eventId and patent back-references, so one call reconciles an invoice spanning many patents:

bash
curl "https://api.renewr.example/api/v2/external/invoices/e5b8c1d7-3f2a-4e9b-a6d4-8c0f1b7e2a53" \
  -H "x-api-key: $RENEWR_API_KEY"
json
{
  "object": "invoice",
  "id": "e5b8c1d7-3f2a-4e9b-a6d4-8c0f1b7e2a53",
  "reference": "INV-2026-0142",
  "number": "142",
  "issueDate": "2026-09-02",
  "origin": "GENERATED",
  "ipRightType": "PATENT",
  "currency": "EUR",
  "vatRate": 0.2,
  "subtotalExcludingTax": "2735.00",
  "totalIncludingTax": "3282.00",
  "fees": [
    {
      "object": "fees",
      "feesStatus": "INVOICED",
      "isFxRateFrozen": false,
      "invoiceId": "e5b8c1d7-3f2a-4e9b-a6d4-8c0f1b7e2a53",
      "eventId": "3f8a1c2e-9b4d-4e6f-8a2b-1c9d7e5f3a10",
      "patent": {
        "id": "b1e6f0a2-4c8d-4b3a-9e7f-2d5c8a1b4e6d",
        "providerId": "ACME-EP-000123"
      },
      "currency": "EUR",
      "total": "1250.00",
      "components": [
        { "type": "OFFICE", "amount": "800.00" },
        { "type": "AGENT", "amount": "150.00" },
        { "type": "RENEWR", "amount": "300.00" }
      ]
    },
    {
      "object": "fees",
      "feesStatus": "INVOICED",
      "isFxRateFrozen": false,
      "invoiceId": "e5b8c1d7-3f2a-4e9b-a6d4-8c0f1b7e2a53",
      "eventId": "a94d7c31-2e5b-4f8a-b6c0-d13e9f57a2b8",
      "patent": {
        "id": "c7d2e8f4-1a6b-4c9d-8e3f-5b0a7d2c9e14",
        "providerId": "ACME-EP-000342"
      },
      "currency": "EUR",
      "total": "1485.00",
      "components": [
        { "type": "OFFICE", "amount": "990.00" },
        { "type": "AGENT", "amount": "175.00" },
        { "type": "RENEWR", "amount": "320.00" }
      ]
    }
  ]
}

The reverse lookup works too: once invoiced, GET /patents/{patentId}/events/{eventId}/fees flips to feesStatus: "INVOICED" with invoiceId set, in the same shape as the estimate, so your reconciliation code handles both. A per-event invoice PDF is available at GET /patents/{patentId}/events/{eventId}/documents/invoice. The full fee model (components, currencies, and estimate drift) is covered in Fees and FX.

Cheat sheet

StageCall
1. Pull the month's dues (with estimates)GET /patent-events?dueMonth=…&include=fees
2. Decideinstructability on each event, via GET /patents/{patentId}/events/{eventId}
3. Instruct (batch, one event per patent)POST /patent-instructions
4. Proof of orderGET …/documents/instruction-receipt
5. Track execution (processingStatus)GET /patents/{patentId}/events/{eventId}
6. Proof of payment (when FULFILLED)GET …/documents/official-receipt
7. ReconcileGET /invoicesGET /invoices/{invoiceId}, per event GET …/fees + GET …/documents/invoice
Optional: FX certainty for a due monthPUT /exchange-rate-freezes/{yearMonth}

Coming from the v1 API? The migration guide maps the old endpoints and vocabulary onto this cycle.

Renewr External API v2. Access on invitation.