diff --git a/README.md b/README.md
index 01e35d170..54c4856e7 100644
--- a/README.md
+++ b/README.md
@@ -34,43 +34,43 @@ including [flexible matching](http://docs.pact.io/documentation/matching.html).
 
 <!-- TOC -->
 
-- [Pact Go](#pact-go)
-  - [Introduction](#introduction)
-  - [Table of Contents](#table-of-contents)
-  - [Installation](#installation)
-    - [Installation on \*nix](#installation-on-\nix)
-  - [Using Pact](#using-pact)
-  - [HTTP API Testing](#http-api-testing)
-    - [Consumer Side Testing](#consumer-side-testing)
-    - [Provider API Testing](#provider-api-testing)
-      - [Provider Verification](#provider-verification)
-      - [API with Authorization](#api-with-authorization)
-    - [Publishing pacts to a Pact Broker and Tagging Pacts](#publishing-pacts-to-a-pact-broker-and-tagging-pacts)
-      - [Publishing from Go code](#publishing-from-go-code)
-      - [Publishing Provider Verification Results to a Pact Broker](#publishing-provider-verification-results-to-a-pact-broker)
-      - [Publishing from the CLI](#publishing-from-the-cli)
-      - [Using the Pact Broker with Basic authentication](#using-the-pact-broker-with-basic-authentication)
-  - [Asynchronous API Testing](#asynchronous-api-testing)
-    - [Consumer](#consumer)
-    - [Provider (Producer)](#provider-producer)
-    - [Pact Broker Integration](#pact-broker-integration)
-  - [Matching](#matching)
-    - [Matching on types](#matching-on-types)
-    - [Matching on arrays](#matching-on-arrays)
-    - [Matching by regular expression](#matching-by-regular-expression)
-    - [Match common formats](#match-common-formats)
-  - [Examples](#examples)
-    - [HTTP APIs](#http-apis)
-    - [Asynchronous APIs](#asynchronous-apis)
-    - [Integrated examples](#integrated-examples)
-  - [Troubleshooting](#troubleshooting)
-      - [Splitting tests across multiple files](#splitting-tests-across-multiple-files)
-    - [Output Logging](#output-logging)
-  - [Contact](#contact)
-  - [Documentation](#documentation)
-  - [Troubleshooting](#troubleshooting-1)
-  - [Roadmap](#roadmap)
-  - [Contributing](#contributing)
+* [Introduction](#introduction)
+* [Table of Contents](#table-of-contents)
+* [Installation](#installation)
+  * [Installation on \*nix](#installation-on-\nix)
+* [Using Pact](#using-pact)
+* [HTTP API Testing](#http-api-testing)
+  * [Consumer Side Testing](#consumer-side-testing)
+  * [Provider API Testing](#provider-api-testing)
+    * [Provider Verification](#provider-verification)
+    * [API with Authorization](#api-with-authorization)
+  * [Publishing pacts to a Pact Broker and Tagging Pacts](#publishing-pacts-to-a-pact-broker-and-tagging-pacts)
+    * [Publishing from Go code](#publishing-from-go-code)
+    * [Publishing Provider Verification Results to a Pact Broker](#publishing-provider-verification-results-to-a-pact-broker)
+    * [Publishing from the CLI](#publishing-from-the-cli)
+    * [Using the Pact Broker with Basic authentication](#using-the-pact-broker-with-basic-authentication)
+* [Asynchronous API Testing](#asynchronous-api-testing)
+  * [Consumer](#consumer)
+  * [Provider (Producer)](#provider-producer)
+  * [Pact Broker Integration](#pact-broker-integration)
+* [Matching](#matching)
+  * [Matching on types](#matching-on-types)
+  * [Matching on arrays](#matching-on-arrays)
+  * [Matching by regular expression](#matching-by-regular-expression)
+  * [Match common formats](#match-common-formats)
+    * [Auto-generate matchers from struct tags](#auto-generate-matchers-from-struct-tags)
+* [Examples](#examples)
+  * [HTTP APIs](#http-apis)
+  * [Asynchronous APIs](#asynchronous-apis)
+  * [Integrated examples](#integrated-examples)
+* [Troubleshooting](#troubleshooting)
+  * [Splitting tests across multiple files](#splitting-tests-across-multiple-files)
+  * [Output Logging](#output-logging)
+* [Contact](#contact)
+* [Documentation](#documentation)
+* [Troubleshooting](#troubleshooting-1)
+* [Roadmap](#roadmap)
+* [Contributing](#contributing)
 
 <!-- /TOC -->
 
@@ -469,7 +469,7 @@ func TestMessageConsumer_Success(t *testing.T) {
 
 **Explanation**:
 
-1.  The  API - a contrived API handler example. Expects a User object and throws an `Error` if it can't handle it.
+1.  The API - a contrived API handler example. Expects a User object and throws an `Error` if it can't handle it.
     * In most applications, some form of transactionality exists and communication with a MQ/broker happens.
     * It's important we separate out the protocol bits from the message handling bits, so that we can test that in isolation.
 1.  Creates the MessageConsumer class
@@ -597,6 +597,50 @@ Often times, you find yourself having to re-write regular expressions for common
 | `IPv6Address()`                                                | Match string containing IP6 formatted address                                                   |
 | `UUID()`                                                       | Match strings containing UUIDs                                                                  |
 
+#### Auto-generate matchers from struct tags
+
+Furthermore, if you isolate your Data Transfer Objects (DTOs) to an adapters package so that they exactly reflect the interface between you and your provider, then you can leverage `dsl.Match` to auto-generate the expected response body in your contract tests. Under the hood, `Match` recursively traverses the DTO struct and uses `Term, Like, and EachLike` to create the contract.
+
+This saves the trouble of declaring the contract by hand. It also maintains one source of truth. To change the consumer-provider interface, you only have to update your DTO struct and the contract will automatically follow suit.
+
+_Example:_
+
+```go
+type DTO struct {
+  ID    string    `json:"id"`
+  Title string    `json:"title"`
+  Tags  []string  `json:"tags" pact:"min=2"`
+  Date  string    `json:"date" pact:"example=2000-01-01,regex=^\\d{4}-\\d{2}-\\d{2}$"`
+}
+```
+
+then specifying a response body is as simple as:
+
+```go
+	// Set up our expected interactions.
+	pact.
+		AddInteraction().
+		Given("User foo exists").
+		UponReceiving("A request to get foo").
+		WithRequest(dsl.Request{
+			Method:  "GET",
+			Path:    "/foobar",
+			Headers: map[string]string{"Content-Type": "application/json"},
+		}).
+		WillRespondWith(dsl.Response{
+			Status:  200,
+			Headers: map[string]string{"Content-Type": "application/json"},
+			Body:    Match(DTO{}), // That's it!!!
+		})
+```
+
+The `pact` struct tags shown above are optional. By default, dsl.Match just asserts that the JSON shape matches the struct and that the field types match.
+
+See [dsl.Match](https://github.com/pact-foundation/pact-go/blob/master/dsl/matcher.go) for more information.
+
+See the [matcher tests](https://github.com/pact-foundation/pact-go/blob/master/dsl/matcher_test.go)
+for more matching examples.
+
 ## Examples
 
 ### HTTP APIs
diff --git a/dsl/matcher.go b/dsl/matcher.go
index 164a77cd8..1134fcfbc 100644
--- a/dsl/matcher.go
+++ b/dsl/matcher.go
@@ -4,7 +4,9 @@ import (
 	"encoding/json"
 	"fmt"
 	"log"
+	"reflect"
 	"strconv"
+	"strings"
 	"time"
 )
 
@@ -306,3 +308,111 @@ func objectToString(obj interface{}) string {
 		return string(jsonString)
 	}
 }
+
+// Match recursively traverses the provided type and outputs a
+// matcher string for it that is compatible with the Pact dsl.
+// By default, it requires slices to have a minimum of 1 element.
+// For concrete types, it uses `dsl.Like` to assert that types match.
+// Optionally, you may override these defaults by supplying custom
+// pact tags on your structs.
+//
+// Supported Tag Formats
+// Minimum Slice Size: `pact:"min=2"`
+// String RegEx:       `pact:"example=2000-01-01,regex=^\\d{4}-\\d{2}-\\d{2}$"`
+func Match(src interface{}) Matcher {
+	return match(reflect.TypeOf(src), getDefaults())
+}
+
+// match recursively traverses the provided type and outputs a
+// matcher string for it that is compatible with the Pact dsl.
+func match(srcType reflect.Type, params params) Matcher {
+	switch kind := srcType.Kind(); kind {
+	case reflect.Ptr:
+		return match(srcType.Elem(), params)
+	case reflect.Slice, reflect.Array:
+		return EachLike(match(srcType.Elem(), getDefaults()), params.slice.min)
+	case reflect.Struct:
+		result := make(map[string]interface{})
+
+		for i := 0; i < srcType.NumField(); i++ {
+			field := srcType.Field(i)
+			result[field.Tag.Get("json")] = match(field.Type, pluckParams(field.Type, field.Tag.Get("pact")))
+		}
+		return result
+	case reflect.String:
+		if params.str.regEx != "" {
+			return Term(params.str.example, params.str.regEx)
+		}
+		return Like(`"string"`)
+	case reflect.Bool:
+		return Like(true)
+	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
+		reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
+		reflect.Float32, reflect.Float64:
+		return Like(1)
+	default:
+		panic(fmt.Sprintf("match: unhandled type: %v", srcType))
+	}
+}
+
+// params are plucked from 'pact' struct tags as match() traverses
+// struct fields. They are passed back into match() along with their
+// associated type to serve as parameters for the dsl functions.
+type params struct {
+	slice sliceParams
+	str   stringParams
+}
+
+type sliceParams struct {
+	min int
+}
+
+type stringParams struct {
+	example string
+	regEx   string
+}
+
+// getDefaults returns the default params
+func getDefaults() params {
+	return params{
+		slice: sliceParams{
+			min: 1,
+		},
+	}
+}
+
+// pluckParams converts a 'pact' tag into a pactParams struct
+// Supported Tag Formats
+// Minimum Slice Size: `pact:"min=2"`
+// String RegEx:       `pact:"example=2000-01-01,regex=^\\d{4}-\\d{2}-\\d{2}$"`
+func pluckParams(srcType reflect.Type, pactTag string) params {
+	params := getDefaults()
+	if pactTag == "" {
+		return params
+	}
+
+	switch kind := srcType.Kind(); kind {
+	case reflect.Slice:
+		if _, err := fmt.Sscanf(pactTag, "min=%d", &params.slice.min); err != nil {
+			triggerInvalidPactTagPanic(pactTag, err)
+		}
+	case reflect.String:
+		components := strings.Split(pactTag, ",regex=")
+
+		if len(components) != 2 {
+			triggerInvalidPactTagPanic(pactTag, fmt.Errorf("invalid format: unable to split on ',regex='"))
+		} else if len(components[1]) == 0 {
+			triggerInvalidPactTagPanic(pactTag, fmt.Errorf("invalid format: regex must not be empty"))
+		} else if _, err := fmt.Sscanf(components[0], "example=%s", &params.str.example); err != nil {
+			triggerInvalidPactTagPanic(pactTag, err)
+		}
+
+		params.str.regEx = strings.Replace(components[1], `\`, `\\`, -1)
+	}
+
+	return params
+}
+
+func triggerInvalidPactTagPanic(tag string, err error) {
+	panic(fmt.Sprintf("match: encountered invalid pact tag %q . . . parsing failed with error: %v", tag, err))
+}
diff --git a/dsl/matcher_test.go b/dsl/matcher_test.go
index f87809c86..ea65bf45b 100644
--- a/dsl/matcher_test.go
+++ b/dsl/matcher_test.go
@@ -524,3 +524,376 @@ func ExampleEachLike() {
 	//	"min": 1
 	//}
 }
+
+func TestMatch(t *testing.T) {
+	type wordDTO struct {
+		Word   string `json:"word"`
+		Length int    `json:"length"`
+	}
+	type dateDTO struct {
+		Date string `json:"date" pact:"example=2000-01-01,regex=^\\d{4}-\\d{2}-\\d{2}$"`
+	}
+	type wordsDTO struct {
+		Words []string `json:"words" pact:"min=2"`
+	}
+	str := "str"
+	type args struct {
+		src interface{}
+	}
+	tests := []struct {
+		name      string
+		args      args
+		want      Matcher
+		wantPanic bool
+	}{
+		{
+			name: "recursive case - ptr",
+			args: args{
+				src: &str,
+			},
+			want: Like(`"string"`),
+		},
+		{
+			name: "recursive case - slice",
+			args: args{
+				src: []string{},
+			},
+			want: EachLike(Like(`"string"`), 1),
+		},
+		{
+			name: "recursive case - array",
+			args: args{
+				src: [1]string{},
+			},
+			want: EachLike(Like(`"string"`), 1),
+		},
+		{
+			name: "recursive case - struct",
+			args: args{
+				src: wordDTO{},
+			},
+			want: map[string]interface{}{
+				"word":   Like(`"string"`),
+				"length": Like(1),
+			},
+		},
+		{
+			name: "recursive case - struct with custom string tag",
+			args: args{
+				src: dateDTO{},
+			},
+			want: map[string]interface{}{
+				"date": Term("2000-01-01", `^\\d{4}-\\d{2}-\\d{2}$`),
+			},
+		},
+		{
+			name: "recursive case - struct with custom slice tag",
+			args: args{
+				src: wordsDTO{},
+			},
+			want: map[string]interface{}{
+				"words": EachLike(Like(`"string"`), 2),
+			},
+		},
+		{
+			name: "base case - string",
+			args: args{
+				src: "string",
+			},
+			want: Like(`"string"`),
+		},
+		{
+			name: "base case - bool",
+			args: args{
+				src: true,
+			},
+			want: Like(true),
+		},
+		{
+			name: "base case - int",
+			args: args{
+				src: 1,
+			},
+			want: Like(1),
+		},
+		{
+			name: "base case - int8",
+			args: args{
+				src: int8(1),
+			},
+			want: Like(1),
+		},
+		{
+			name: "base case - int16",
+			args: args{
+				src: int16(1),
+			},
+			want: Like(1),
+		},
+		{
+			name: "base case - int32",
+			args: args{
+				src: int32(1),
+			},
+			want: Like(1),
+		},
+		{
+			name: "base case - int64",
+			args: args{
+				src: int64(1),
+			},
+			want: Like(1),
+		},
+		{
+			name: "base case - uint",
+			args: args{
+				src: uint(1),
+			},
+			want: Like(1),
+		},
+		{
+			name: "base case - uint8",
+			args: args{
+				src: uint8(1),
+			},
+			want: Like(1),
+		},
+		{
+			name: "base case - uint16",
+			args: args{
+				src: uint16(1),
+			},
+			want: Like(1),
+		},
+		{
+			name: "base case - uint32",
+			args: args{
+				src: uint32(1),
+			},
+			want: Like(1),
+		},
+		{
+			name: "base case - uint64",
+			args: args{
+				src: uint64(1),
+			},
+			want: Like(1),
+		},
+		{
+			name: "base case - float32",
+			args: args{
+				src: float32(1),
+			},
+			want: Like(1),
+		},
+		{
+			name: "base case - float64",
+			args: args{
+				src: float64(1),
+			},
+			want: Like(1),
+		},
+		{
+			name: "error - unhandled type",
+			args: args{
+				src: make(map[string]string),
+			},
+			wantPanic: true,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			var got Matcher
+			var didPanic bool
+			defer func() {
+				if rec := recover(); rec != nil {
+					fmt.Println(rec)
+					didPanic = true
+				}
+				if tt.wantPanic != didPanic {
+					t.Errorf("Match() - '%s': didPanic = %v, want %v", tt.name, didPanic, tt.wantPanic)
+				} else if !didPanic && !reflect.DeepEqual(got, tt.want) {
+					t.Errorf("Match() - '%s':  = %v, want %v", tt.name, got, tt.want)
+				}
+			}()
+
+			got = Match(tt.args.src)
+			log.Println("Got matcher: ", got)
+		})
+	}
+}
+
+func Test_pluckParams(t *testing.T) {
+	type args struct {
+		srcType reflect.Type
+		pactTag string
+	}
+	tests := []struct {
+		name      string
+		args      args
+		want      params
+		wantPanic bool
+	}{
+		{
+			name: "expected use - slice tag",
+			args: args{
+				srcType: reflect.TypeOf([]string{}),
+				pactTag: "min=2",
+			},
+			want: params{
+				slice: sliceParams{
+					min: 2,
+				},
+				str: stringParams{
+					example: getDefaults().str.example,
+					regEx:   getDefaults().str.regEx,
+				},
+			},
+		},
+		{
+			name: "empty slice tag",
+			args: args{
+				srcType: reflect.TypeOf([]string{}),
+				pactTag: "",
+			},
+			want: getDefaults(),
+		},
+		{
+			name: "invalid slice tag - no min",
+			args: args{
+				srcType: reflect.TypeOf([]string{}),
+				pactTag: "min=",
+			},
+			wantPanic: true,
+		},
+		{
+			name: "invalid slice tag - min typo capital letter",
+			args: args{
+				srcType: reflect.TypeOf([]string{}),
+				pactTag: "Min=2",
+			},
+			wantPanic: true,
+		},
+		{
+			name: "invalid slice tag - min typo non-number",
+			args: args{
+				srcType: reflect.TypeOf([]string{}),
+				pactTag: "min=a",
+			},
+			wantPanic: true,
+		},
+		{
+			name: "expected use - string tag",
+			args: args{
+				srcType: reflect.TypeOf(""),
+				pactTag: "example=aBcD123,regex=[A-Za-z0-9]",
+			},
+			want: params{
+				slice: sliceParams{
+					min: getDefaults().slice.min,
+				},
+				str: stringParams{
+					example: "aBcD123",
+					regEx:   "[A-Za-z0-9]",
+				},
+			},
+		},
+		{
+			name: "expected use - string tag with backslash",
+			args: args{
+				srcType: reflect.TypeOf(""),
+				pactTag: "example=33,regex=\\d{2}",
+			},
+			want: params{
+				slice: sliceParams{
+					min: getDefaults().slice.min,
+				},
+				str: stringParams{
+					example: "33",
+					regEx:   `\\d{2}`,
+				},
+			},
+		},
+		{
+			name: "empty string tag",
+			args: args{
+				srcType: reflect.TypeOf(""),
+				pactTag: "",
+			},
+			want: getDefaults(),
+		},
+		{
+			name: "invalid string tag - no example value",
+			args: args{
+				srcType: reflect.TypeOf(""),
+				pactTag: "example=,regex=[A-Za-z0-9]",
+			},
+			wantPanic: true,
+		},
+		{
+			name: "invalid string tag - no example",
+			args: args{
+				srcType: reflect.TypeOf(""),
+				pactTag: "regex=[A-Za-z0-9]",
+			},
+			wantPanic: true,
+		},
+		{
+			name: "invalid string tag - example typo",
+			args: args{
+				srcType: reflect.TypeOf(""),
+				pactTag: "exmple=aBcD123,regex=[A-Za-z0-9]",
+			},
+			wantPanic: true,
+		},
+		{
+			name: "invalid string tag - no regex value",
+			args: args{
+				srcType: reflect.TypeOf(""),
+				pactTag: "example=aBcD123,regex=",
+			},
+			wantPanic: true,
+		},
+		{
+			name: "invalid string tag - no regex",
+			args: args{
+				srcType: reflect.TypeOf(""),
+				pactTag: "example=aBcD123",
+			},
+			wantPanic: true,
+		},
+		{
+			name: "invalid string tag - regex typo",
+			args: args{
+				srcType: reflect.TypeOf(""),
+				pactTag: "example=aBcD123,regx=[A-Za-z0-9]",
+			},
+			wantPanic: true,
+		},
+		{
+			name: "invalid string tag - space inserted",
+			args: args{
+				srcType: reflect.TypeOf(""),
+				pactTag: "example=aBcD123 regex=[A-Za-z0-9]",
+			},
+			wantPanic: true,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			var got params
+			var didPanic bool
+			defer func() {
+				if rec := recover(); rec != nil {
+					didPanic = true
+				}
+				if tt.wantPanic != didPanic {
+					t.Errorf("pluckParams() didPanic = %v, want %v", didPanic, tt.wantPanic)
+				} else if !didPanic && !reflect.DeepEqual(got, tt.want) {
+					t.Errorf("pluckParams() = %v, want %v", got, tt.want)
+				}
+			}()
+			got = pluckParams(tt.args.srcType, tt.args.pactTag)
+		})
+	}
+}