dsl struct tags; wip
diff --git a/params.go b/params.go
index 85640ec..5e7f5d0 100644
--- a/params.go
+++ b/params.go
@@ -10,10 +10,11 @@
 	"time"
 )
 
-// BuildRequestBody builds a map[string]interface from the given `struct`.
+// BuildRequestBody builds a map[string]interface from the given `struct`. If
+// parent is not the empty string, the final map[string]interface returned will
+// encapsulate the built one
 //
-//
-func BuildRequestBody(opts interface{}) (map[string]interface{}, error) {
+func BuildRequestBody(opts interface{}, parent string) (map[string]interface{}, error) {
 	optsValue := reflect.ValueOf(opts)
 	if optsValue.Kind() == reflect.Ptr {
 		optsValue = optsValue.Elem()
@@ -26,30 +27,125 @@
 
 	optsMap := make(map[string]interface{})
 	if optsValue.Kind() == reflect.Struct {
-
+		//fmt.Printf("optsValue.Kind() is a reflect.Struct: %+v\n", optsValue.Kind())
 		for i := 0; i < optsValue.NumField(); i++ {
 			v := optsValue.Field(i)
 			f := optsType.Field(i)
-			requiredTag := f.Tag.Get("required")
 
-			// if the field has a 'required' tag, it can't have a zero-value
-			if requiredTag == "true" && isZero(v) {
-				err := ErrMissingInput{}
-				err.Argument = f.Name
-				return nil, err
+			fmt.Printf("Starting on field: %s...\n", f.Name)
+
+			zero := isZero(v)
+			fmt.Printf("v is zero?: %v\n", zero)
+
+			// if there are 0 tags or if there is only 1 and it's the json tag,
+			// we don't need to do anything for this field
+			//if len(strings.Split(string(f.Tag), " ")) < 2 && f.Tag.Get("json") != "" && zero {
+			//	fmt.Printf("skipping field: %s with tag: %+v\n", f.Name, f.Tag)
+			//	continue
+			//}
+
+			// if the field has a required tag that's set to "true"
+			if requiredTag := f.Tag.Get("required"); requiredTag == "true" {
+				fmt.Printf("Checking required field [%s]:\n\tv: %+v\n\tisZero:%v\n", f.Name, v.Interface(), zero)
+				// if the field's value is zero, return a missing-argument error
+				if zero {
+					// if the field has a 'required' tag, it can't have a zero-value
+					err := ErrMissingInput{}
+					err.Argument = f.Name
+					return nil, err
+				}
+			}
+
+			if xorTag := f.Tag.Get("xor"); xorTag != "" {
+				fmt.Printf("Checking `xor` tag for field [%s] with value %+v:\n\txorTag: %s\n", f.Name, v, xorTag)
+				xorField := optsValue.FieldByName(xorTag)
+				var xorFieldIsZero bool
+				if reflect.ValueOf(xorField.Interface()) == reflect.Zero(xorField.Type()) {
+					xorFieldIsZero = true
+				} else {
+					if xorField.Kind() == reflect.Ptr {
+						xorField = xorField.Elem()
+					}
+					xorFieldIsZero = isZero(xorField)
+				}
+				if !(zero != xorFieldIsZero) {
+					err := ErrMissingInput{}
+					err.Argument = fmt.Sprintf("%s/%s", f.Name, xorTag)
+					err.Info = fmt.Sprintf("Exactly one of %s and %s must be provided", f.Name, xorTag)
+					return nil, err
+				}
+			}
+
+			if orTag := f.Tag.Get("or"); orTag != "" {
+				fmt.Printf("Checking `or` tag for field with:\n\tname: %+v\n\torTag:%s\n", f.Name, orTag)
+				fmt.Printf("field is zero?: %v\n", zero)
+				if zero {
+					orField := optsValue.FieldByName(orTag)
+					var orFieldIsZero bool
+					if reflect.ValueOf(orField.Interface()) == reflect.Zero(orField.Type()) {
+						orFieldIsZero = true
+					} else {
+						if orField.Kind() == reflect.Ptr {
+							orField = orField.Elem()
+						}
+						orFieldIsZero = isZero(orField)
+					}
+					if orFieldIsZero {
+						err := ErrMissingInput{}
+						err.Argument = fmt.Sprintf("%s/%s", f.Name, orTag)
+						err.Info = fmt.Sprintf("At least one of %s and %s must be provided", f.Name, orTag)
+						return nil, err
+					}
+				}
+			}
+
+			if v.Kind() == reflect.Struct || (v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.Struct) {
+				if zero {
+					fmt.Printf("value before change: %+v\n", optsValue.Field(i))
+					if jsonTag := f.Tag.Get("json"); jsonTag != "" {
+						jsonTagPieces := strings.Split(jsonTag, ",")
+						if len(jsonTagPieces) > 1 && jsonTagPieces[1] == "omitempty" {
+							if v.CanSet() {
+								if !v.IsNil() {
+									if v.Kind() == reflect.Ptr {
+										v.Set(reflect.Zero(v.Type()))
+									}
+								}
+								fmt.Printf("value after change: %+v\n", optsValue.Field(i))
+							}
+						}
+					}
+					continue
+				}
+
+				fmt.Printf("Calling BuildRequestBody with:\n\tv: %+v\n\tf.Name:%s\n", v.Interface(), f.Name)
+				_, err := BuildRequestBody(v.Interface(), f.Name)
+				if err != nil {
+					return nil, err
+				}
 			}
 		}
 
+		fmt.Printf("opts: %+v \n", opts)
+
 		b, err := json.Marshal(opts)
 		if err != nil {
 			return nil, err
 		}
 
+		fmt.Printf("string(b): %s\n", string(b))
+
 		err = json.Unmarshal(b, &optsMap)
 		if err != nil {
 			return nil, err
 		}
 
+		//fmt.Printf("optsMap: %+v\n", optsMap)
+
+		if parent != "" {
+			optsMap = map[string]interface{}{parent: optsMap}
+		}
+		//fmt.Printf("optsMap after parent added: %+v\n", optsMap)
 		return optsMap, nil
 	}
 	// Return an error if the underlying type of 'opts' isn't a struct.
@@ -107,10 +203,26 @@
 	return nil
 }
 
+/*
+func isUnderlyingStructZero(v reflect.Value) bool {
+	switch v.Kind() {
+	case reflect.Ptr:
+		return isUnderlyingStructZero(v.Elem())
+	default:
+		return isZero(v)
+	}
+}
+*/
+
 var t time.Time
 
 func isZero(v reflect.Value) bool {
 	switch v.Kind() {
+	case reflect.Ptr:
+		if v.IsNil() {
+			return true
+		}
+		return isZero(v.Elem())
 	case reflect.Func, reflect.Map, reflect.Slice:
 		return v.IsNil()
 	case reflect.Array: