diff --git a/build.gradle b/build.gradle
index 89c3b0cc3..b26b72af4 100644
--- a/build.gradle
+++ b/build.gradle
@@ -32,6 +32,10 @@ project.version = "1.10.5-SNAPSHOT" // {x-version-update:api-common:current}
 sourceCompatibility = 1.7
 targetCompatibility = 1.7
 
+jacoco {
+    toolVersion = "0.8.7"
+}
+
 // Dependencies
 // ------------
 
diff --git a/src/main/java/com/google/api/pathtemplate/PathTemplate.java b/src/main/java/com/google/api/pathtemplate/PathTemplate.java
index dd133461a..098524210 100644
--- a/src/main/java/com/google/api/pathtemplate/PathTemplate.java
+++ b/src/main/java/com/google/api/pathtemplate/PathTemplate.java
@@ -664,7 +664,8 @@ private boolean match(
             switch (segments.get(i).kind()) {
               case BINDING:
               case END_BINDING:
-                // skip
+              case CUSTOM_VERB:
+                // These segments do not actually consume any input.
                 continue;
               default:
                 segsToMatch++;
@@ -746,7 +747,8 @@ private String instantiate(Map<String, String> values, boolean allowPartial) {
     while (iterator.hasNext()) {
       Segment seg = iterator.next();
       if (!skip && !continueLast) {
-        String separator = prevSeparator.isEmpty() ? seg.separator() : prevSeparator;
+        String separator =
+            prevSeparator.isEmpty() || !iterator.hasNext() ? seg.separator() : prevSeparator;
         result.append(separator);
         prevSeparator = seg.complexSeparator().isEmpty() ? seg.separator() : seg.complexSeparator();
       }
diff --git a/src/test/java/com/google/api/pathtemplate/PathTemplateTest.java b/src/test/java/com/google/api/pathtemplate/PathTemplateTest.java
index dc90b65ba..5c6a83858 100644
--- a/src/test/java/com/google/api/pathtemplate/PathTemplateTest.java
+++ b/src/test/java/com/google/api/pathtemplate/PathTemplateTest.java
@@ -114,6 +114,7 @@ public void pathWildcards_matchZeroOrMoreSegments() {
     PathTemplate start = PathTemplate.create("{glob=**}/b");
     PathTemplate middle = PathTemplate.create("a/{glob=**}/b");
     PathTemplate end = PathTemplate.create("a/{glob=**}");
+    PathTemplate endWithCustomVerb = PathTemplate.create("a/{glob=**}:foo");
 
     Truth.assertThat(start.match("b").get("glob")).isEmpty();
     Truth.assertThat(start.match("/b").get("glob")).isEmpty();
@@ -129,6 +130,10 @@ public void pathWildcards_matchZeroOrMoreSegments() {
     Truth.assertThat(end.match("a/").get("glob")).isEmpty();
     Truth.assertThat(end.match("a/b").get("glob")).isEqualTo("b");
     Truth.assertThat(end.match("a/b/b/b").get("glob")).isEqualTo("b/b/b");
+
+    Truth.assertThat(endWithCustomVerb.match("a/:foo").get("glob")).isEmpty();
+    Truth.assertThat(endWithCustomVerb.match("a/b:foo").get("glob")).isEqualTo("b");
+    Truth.assertThat(endWithCustomVerb.match("a/b/b:foo").get("glob")).isEqualTo("b/b");
   }
 
   @Test
@@ -173,6 +178,12 @@ public void matchWithUnboundInMiddle() {
     assertPositionalMatch(template.match("bar/foo/foo/foo/bar"), "foo/foo", "bar");
   }
 
+  @Test
+  public void matchWithCustomVerbs() {
+    PathTemplate template = PathTemplate.create("**:foo");
+    assertPositionalMatch(template.match("a/b/c:foo"), "a/b/c");
+  }
+
   // Complex Resource ID Segments.
   // ========
 
@@ -215,6 +226,45 @@ public void complexResourceIdBasicCases() {
     Truth.assertThat(match.get("zone_b")).isEqualTo("us-east3-a");
   }
 
+  @Test
+  public void complexResourceIdCustomVerb() {
+    // Separate by "~".
+    PathTemplate template = PathTemplate.create("projects/{project}/zones/{zone_a}~{zone_b}:hello");
+    Map<String, String> match =
+        template.match(
+            "https://www.googleapis.com/compute/v1/projects/project-123/zones/europe-west3-c~us-east3-a:hello");
+    Truth.assertThat(match).isNotNull();
+    Truth.assertThat(match.get(PathTemplate.HOSTNAME_VAR)).isEqualTo("https://www.googleapis.com");
+    Truth.assertThat(match.get("project")).isEqualTo("project-123");
+    Truth.assertThat(match.get("zone_a}~{zone_b")).isNull();
+    Truth.assertThat(match.get("zone_a")).isEqualTo("europe-west3-c");
+    Truth.assertThat(match.get("zone_b")).isEqualTo("us-east3-a");
+
+    // Separate by "-".
+    template = PathTemplate.create("projects/{project}/zones/{zone_a}-{zone_b}:hello");
+    match = template.match("projects/project-123/zones/europe-west3-c~us-east3-a:hello");
+    Truth.assertThat(match).isNotNull();
+    Truth.assertThat(match.get("project")).isEqualTo("project-123");
+    Truth.assertThat(match.get("zone_a")).isEqualTo("europe");
+    Truth.assertThat(match.get("zone_b")).isEqualTo("west3-c~us-east3-a");
+
+    // Separate by ".".
+    template = PathTemplate.create("projects/{project}/zones/{zone_a}.{zone_b}:hello");
+    match = template.match("projects/project-123/zones/europe-west3-c.us-east3-a:hello");
+    Truth.assertThat(match).isNotNull();
+    Truth.assertThat(match.get("project")).isEqualTo("project-123");
+    Truth.assertThat(match.get("zone_a")).isEqualTo("europe-west3-c");
+    Truth.assertThat(match.get("zone_b")).isEqualTo("us-east3-a");
+
+    // Separate by "_".
+    template = PathTemplate.create("projects/{project}/zones/{zone_a}_{zone_b}:hello");
+    match = template.match("projects/project-123/zones/europe-west3-c_us-east3-a:hello");
+    Truth.assertThat(match).isNotNull();
+    Truth.assertThat(match.get("project")).isEqualTo("project-123");
+    Truth.assertThat(match.get("zone_a")).isEqualTo("europe-west3-c");
+    Truth.assertThat(match.get("zone_b")).isEqualTo("us-east3-a");
+  }
+
   @Test
   public void complexResourceIdEqualsWildcard() {
     PathTemplate template = PathTemplate.create("projects/{project=*}/zones/{zone_a=*}~{zone_b=*}");
@@ -604,6 +654,18 @@ public void instantiateWithComplexResourceId_basic() {
     Truth.assertThat(instance).isEqualTo("projects/a%2Fb%2Fc/zones/apple~baseball");
   }
 
+  @Test
+  public void instantiateWithComplexResourceId_customVerb() {
+    PathTemplate template = PathTemplate.create("projects/{project}/zones/{zone_a}~{zone_b}:hello");
+    String instance =
+        template.instantiate("project", "a/b/c", "zone_a", "apple", "zone_b", "baseball");
+    Truth.assertThat(instance).isEqualTo("projects/a%2Fb%2Fc/zones/apple~baseball:hello");
+
+    template = PathTemplate.create("projects/{project}/zones/{zone_a}~{zone_b}/stuff:hello");
+    instance = template.instantiate("project", "a/b/c", "zone_a", "apple", "zone_b", "baseball");
+    Truth.assertThat(instance).isEqualTo("projects/a%2Fb%2Fc/zones/apple~baseball/stuff:hello");
+  }
+
   @Test
   public void instantiateWithComplexResourceId_mixedSeparators() {
     PathTemplate template =
@@ -647,6 +709,14 @@ public void instantiateWithComplexResourceId_mixedSeparatorsInParent() {
     Truth.assertThat(instance).isEqualTo("projects/a%2Fb%2Fc~foo.bar/zones/apple~baseball");
   }
 
+  @Test
+  public void instantiateWithCustomVerbs() {
+    PathTemplate template = PathTemplate.create("/v1/{name=operations/**}:cancel");
+    String templateInstance = template.instantiate("name", "operations/3373707");
+    Truth.assertThat(templateInstance).isEqualTo("v1/operations/3373707:cancel");
+    Truth.assertThat(template.matches(templateInstance));
+  }
+
   // Other
   // =====