REST v2 API coverage gaps

We’re migrating our integration from XMLRPC to the REST API (v2 where possible, falling back to v1 where needed). The migration is going well, but we’ve identified several areas where v2 doesn’t yet have equivalent endpoints, forcing us to mix v1 and v2 calls. We wanted to flag these in case v2 additions are planned, and to ask one question about authentication.

Endpoints where we’re falling back to REST v1:

  1. Opportunities by contact — No way to filter opportunities by contact_id in v2. We’re using GET /v1/opportunities?contact_id={id}.
  2. Pipeline stages — No v2 endpoint for pipeline stage definitions. We’re using GET /v1/opportunity/stage_pipeline.
  3. Users — No v2 endpoint for user lookup. We’re using GET /v1/users and GET /v1/users/{id}.
  4. Email opt-in — The v2 PATCH /v2/contacts/{id} doesn’t accept opt_in_reason as a top-level field. We’re using PATCH /v1/contacts/{id} with opt_in_reason to opt in contacts. There also doesn’t appear to be any REST endpoint (v1 or v2) for opting out a contact — the old XMLRPC APIEmailService.optOut was the only way to do this.
  5. Contact create with duplicate_option — The v2 POST /v2/contacts doesn’t support the duplicate_option parameter that v1 had (Email, EmailAndName, etc.). We’ve implemented a search-then-create pattern as a workaround, but native duplicate handling would be cleaner.
  6. Custom field filtering — v2 contact filters support email, given_name, family_name, company_id, etc., but there’s no way to filter contacts by custom field values. This isn’t blocking for us currently, but we wanted to mention it.
    v2 behavior note — custom_fields on PATCH
    We discovered that PATCH /v2/contacts/{id} treats the custom_fields array as a full replacement — any field not included in the array is removed from the contact. The old XMLRPC update was a merge (only specified fields were changed). We’ve worked around this with a read-merge-write pattern (GET current fields, merge our updates, PATCH the complete array), but wanted to confirm this is the intended behavior and not a bug.

I’ve attempted to create a ticket with this info twice, but after submission, no ticket is generated.

Hi @Dan_Heinz,

Thanks for reaching out and for the thorough breakdown.

  1. This is one of the gaps we’ve identified in v2, and the team is actively looking into it. It’s tracked as item #52 in the known issues tracker.
  2. We do have an equivalent v2 endpoint for pipeline stages. You can find the documentation here: Keap REST API
  3. We also have user lookup endpoints available in v2: GET /v2/users and GET /v2/users/{user_id}. Full documentation is here: Keap REST API
  4. I’ll bring this up internally so we can take a closer look at the opt_in_reason field. That field is available in v1 but not at the contact level in v2. We do have an endpoint to update the opt-in status, you can opt in or opt out an email address using this endpoint. Documentation: Keap REST API
  5. This is another gap we’ve identified and it’s currently listed as item #3 in the known issues tracker.
  6. At this time, this functionality isn’t available in v2 due to performance considerations.

Regarding the v2 behavior note: yes, that is the intended behavior.

Please let me know if you’d like to go deeper into any of these, happy to help!