package mysubaru import ( "encoding/json" "errors" "fmt" "log/slog" "time" ) // Response represents the structure of a response from the MySubaru API. type Response struct { Success bool `json:"success"` // true | false ErrorCode string `json:"errorCode,omitempty"` // string | Error message if Success is false DataName string `json:"dataName,omitempty"` // string | Describes the structure which is included in Data field Data json.RawMessage `json:"data"` // Data struct } // parse parses the JSON response from the MySubaru API into a Response struct. func (r *Response) parse(b []byte, logger *slog.Logger) (*Response, error) { err := json.Unmarshal(b, &r) if err != nil { logger.Error("error while parsing json", "error", err.Error()) return nil, errors.New("error while parsing json: " + err.Error()) } if !r.Success && r.ErrorCode != "" { logger.Error("error in response", "errorCode", r.ErrorCode, "dataName", r.DataName) switch r.ErrorCode { // G2 API errors case API_ERRORS["API_ERROR_NO_ACCOUNT"]: return r, errors.New("error in response: Account not found") case API_ERRORS["API_ERROR_INVALID_ACCOUNT"]: return r, errors.New("error in response: Invalid Account") case API_ERRORS["API_ERROR_INVALID_CREDENTIALS"]: return r, errors.New("error in response: Invalid Credentials") case API_ERRORS["API_ERROR_INVALID_TOKEN"]: return r, errors.New("error in response: Invalid Token") case API_ERRORS["API_ERROR_PASSWORD_WARNING"]: return r, errors.New("error in response: Mutiple failed login attempts, password warning") case API_ERRORS["API_ERROR_TOO_MANY_ATTEMPTS"]: return r, errors.New("error in response: Too many attempts, please try again later") case API_ERRORS["API_ERROR_ACCOUNT_LOCKED"]: return r, errors.New("error in response: Account Locked") case API_ERRORS["API_ERROR_NO_VEHICLES"]: return r, errors.New("error in response: No vehicles found for the account") case API_ERRORS["API_ERROR_VEHICLE_SETUP"]: return r, errors.New("error in response: Vehicle setup is not complete") case API_ERRORS["API_ERROR_VEHICLE_NOT_IN_ACCOUNT"]: return r, errors.New("error in response: Vehicle not in account") case API_ERRORS["API_ERROR_SERVICE_ALREADY_STARTED"]: return r, errors.New("error in response: Service already started") case API_ERRORS["API_ERROR_SOA_403"]: return r, errors.New("error in response: Unable to parse response body, SOA 403 error") // G1 API errors case API_ERRORS["API_ERROR_G1_NO_SUBSCRIPTION"]: return r, errors.New("error in response: No subscription found for the vehicle") case API_ERRORS["API_ERROR_G1_STOLEN_VEHICLE"]: return r, errors.New("error in response: Car is reported as stolen") case API_ERRORS["API_ERROR_G1_INVALID_PIN"]: return r, errors.New("error in response: Invalid PIN") case API_ERRORS["API_ERROR_G1_PIN_LOCKED"]: return r, errors.New("error in response: PIN is locked") case API_ERRORS["API_ERROR_G1_SERVICE_ALREADY_STARTED"]: return r, errors.New("error in response: Service already started") } return r, errors.New("error in response: " + r.ErrorCode) } return r, nil } // Request represents the structure of a request to the MySubaru API. type Request struct { Vin string `json:"vin"` // Pin string `json:"pin"` // Delay int `json:"delay,string,omitempty"` // ForceKeyInCar *bool `json:"forceKeyInCar,string,omitempty"` // UnlockDoorType *string `json:"unlockDoorType,omitempty"` // [ ALL_DOORS_CMD | FRONT_LEFT_DOOR_CMD | ALL_DOORS_CMD ] Horn *string `json:"horn,omitempty"` // ClimateSettings *string `json:"climateSettings,omitempty"` // ClimateZoneFrontTemp *string `json:"climateZoneFrontTemp,omitempty"` // ClimateZoneFrontAirMode *string `json:"climateZoneFrontAirMode,omitempty"` // ClimateZoneFrontAirVolume *string `json:"climateZoneFrontAirVolume,omitempty"` // HeatedSeatFrontLeft *string `json:"heatedSeatFrontLeft,omitempty"` // HeatedSeatFrontRight *string `json:"heatedSeatFrontRight,omitempty"` // HeatedRearWindowActive *string `json:"heatedRearWindowActive,omitempty"` // OuterAirCirculation *string `json:"outerAirCirculation,omitempty"` // AirConditionOn *string `json:"airConditionOn,omitempty"` // RunTimeMinutes *string `json:"runTimeMinutes,omitempty"` // StartConfiguration *string `json:"startConfiguration,omitempty"` // } // account . type account struct { MarketID int `json:"marketId"` AccountKey int `json:"accountKey"` FirstName string `json:"firstName"` LastName string `json:"lastName"` ZipCode string `json:"zipCode"` ZipCode5 string `json:"zipCode5"` LastLoginDate UnixTime `json:"lastLoginDate"` CreatedDate UnixTime `json:"createdDate"` } // Customer . type Customer struct { SessionCustomer SessionCustomer `json:"sessionCustomer,omitempty"` // struct | Only by performing a RefreshVehicles request Email string `json:"email"` FirstName string `json:"firstName"` LastName string `json:"lastName"` Zip string `json:"zip"` OemCustID string `json:"oemCustId"` Phone string `json:"phone"` } // SessionCustomer . type SessionCustomer struct { FirstName string `json:"firstName,omitempty"` LastName string `json:"lastName,omitempty"` Title string `json:"title,omitempty"` Suffix string `json:"suffix,omitempty"` Email string `json:"email"` Address string `json:"address"` Address2 string `json:"address2,omitempty"` City string `json:"city"` State string `json:"state"` Zip string `json:"zip"` CellularPhone string `json:"cellularPhone,omitempty"` WorkPhone string `json:"workPhone,omitempty"` HomePhone string `json:"homePhone,omitempty"` CountryCode string `json:"countryCode"` RelationshipType any `json:"relationshipType,omitempty"` Gender string `json:"gender,omitempty"` DealerCode any `json:"dealerCode,omitempty"` OemCustID string `json:"oemCustId"` CreateMysAccount any `json:"createMysAccount,omitempty"` SourceSystemCode string `json:"sourceSystemCode"` Vehicles []struct { Vin string `json:"vin"` SiebelVehicleRelationship string `json:"siebelVehicleRelationship"` // TM Subscriber | Previous TM Subscriber | Previous Owner Primary bool `json:"primary"` // true | false OemCustID string `json:"oemCustId"` // CRM-41PLM-5TYE | 1-8K7OBOJ | 1-8JY3UVS | CRM-44UFUA14-V Status string `json:"status,omitempty"` // "Active" | "Draft" | "Inactive" } `json:"vehicles"` Phone string `json:"phone,omitempty"` Zip5Digits string `json:"zip5Digits"` PrimaryPersonalCountry string `json:"primaryPersonalCountry"` } // DataMap . // "dataName": "dataMap" type dataMap struct { Username string `json:"userName"` Email string `json:"email"` } // SessionData . // "dataName": "sessionData" type SessionData struct { Account account `json:"account"` PasswordToken string `json:"passwordToken"` ResetPassword bool `json:"resetPassword"` SessionID string `json:"sessionId"` SessionChanged bool `json:"sessionChanged"` DeviceID string `json:"deviceId"` DeviceRegistered bool `json:"deviceRegistered"` RegisteredDevicePermanent bool `json:"registeredDevicePermanent"` Vehicles []VehicleData `json:"vehicles"` VehicleInactivated bool `json:"vehicleInactivated"` RightToRepairEnabled bool `json:"rightToRepairEnabled"` RightToRepairStates string `json:"rightToRepairStates"` CurrentVehicleIndex int `json:"currentVehicleIndex"` HandoffToken string `json:"handoffToken"` EnableXtime bool `json:"enableXtime"` TermsAndConditionsAccepted bool `json:"termsAndConditionsAccepted"` RightToRepairStartYear int `json:"rightToRepairStartYear"` DigitalGlobeConnectID string `json:"digitalGlobeConnectId"` DigitalGlobeImageTileService string `json:"digitalGlobeImageTileService"` DigitalGlobeTransparentTileService string `json:"digitalGlobeTransparentTileService"` TomtomKey string `json:"tomtomKey"` SatelliteViewEnabled bool `json:"satelliteViewEnabled"` } // Vehicle . // "dataName": "vehicle" type VehicleData struct { Customer Customer `json:"customer"` // Customer struct OemCustID string `json:"oemCustId"` // CRM-631-HQN48K UserOemCustID string `json:"userOemCustId"` // CRM-631-HQN48K Active bool `json:"active"` // true | false Email string `json:"email"` // null | email@address.com FirstName string `json:"firstName,omitempty"` // null | First Name LastName string `json:"lastName,omitempty"` // null | Last Name Zip string `json:"zip"` // 12345 Phone string `json:"phone,omitempty"` // null | 123-456-7890 StolenVehicle bool `json:"stolenVehicle"` // true | false VehicleName string `json:"vehicleName"` // Subaru Outback LXT Features []string `json:"features"` // "11.6MMAN", "ABS_MIL", "ACCS", "AHBL_MIL", "ATF_MIL", "AWD_MIL", "BSD", "BSDRCT_MIL", "CEL_MIL", "EBD_MIL", "EOL_MIL", "EPAS_MIL", "EPB_MIL", "ESS_MIL", "EYESIGHT", "ISS_MIL", "NAV_TOMTOM", "OPL_MIL", "RAB_MIL", "RCC", "REARBRK", "RES", "RESCC", "RHSF", "RPOI", "RPOIA", "SRH_MIL", "SRS_MIL", "TEL_MIL", "TPMS_MIL", "VDC_MIL", "WASH_MIL", "g2" Vin string `json:"vin"` // 4Y1SL65848Z411439 VehicleKey int64 `json:"vehicleKey"` // 3832950 Nickname string `json:"nickname"` // Subaru Outback LXT ModelName string `json:"modelName"` // Outback ModelYear string `json:"modelYear"` // 2020 ModelCode string `json:"modelCode"` // LDJ ExtDescrip string `json:"extDescrip"` // Abyss Blue Pearl (ext color) IntDescrip string `json:"intDescrip"` // Gray (int color) TransCode string `json:"transCode"` // CVT EngineSize float64 `json:"engineSize"` // 2.4 Phev bool `json:"phev"` // null CachedStateCode string `json:"cachedStateCode"` // NJ LicensePlate string `json:"licensePlate"` // NJ LicensePlateState string `json:"licensePlateState"` // ABCDEF SubscriptionStatus string `json:"subscriptionStatus"` // ACTIVE SubscriptionFeatures []string `json:"subscriptionFeatures"` // "[ REMOTE ], [ SAFETY ], [ Retail | Finance3 | RetailPHEV ]"" SubscriptionPlans []string `json:"subscriptionPlans,omitempty"` // [] VehicleGeoPosition GeoPosition `json:"vehicleGeoPosition"` // GeoPosition struct AccessLevel int `json:"accessLevel"` // -1 VehicleMileage int `json:"vehicleMileage,omitempty"` // null CrmRightToRepair bool `json:"crmRightToRepair"` // true | false AuthorizedVehicle bool `json:"authorizedVehicle"` // false | true NeedMileagePrompt bool `json:"needMileagePrompt"` // false | true RemoteServicePinExist bool `json:"remoteServicePinExist"` // true | false NeedEmergencyContactPrompt bool `json:"needEmergencyContactPrompt"` // false | true Show3GSunsetBanner bool `json:"show3gSunsetBanner"` // false | true Provisioned bool `json:"provisioned"` // true | false TimeZone string `json:"timeZone"` // America/New_York SunsetUpgraded bool `json:"sunsetUpgraded"` // true | false PreferredDealer string `json:"preferredDealer,omitempty"` // null | } // GeoPosition . type GeoPosition struct { Latitude float64 `json:"latitude"` // 40.700184 Longitude float64 `json:"longitude"` // -74.401375 Speed float64 `json:"speed,omitempty"` // 62 Heading int `json:"heading,omitempty"` // 155 Timestamp string `json:"timestamp"` // "2021-12-22T13:14:47" } // type GeoPositionTime time.Time // func (g *GeoPositionTime) UnmarshalJSON(b []byte) error { // s := strings.Trim(string(b), "\"") // t, err := time.Parse("2006-01-02T15:04:05", s) // if err != nil { // panic(err) // } // *g = GeoPositionTime(t) // return nil // } // VehicleStatus . type VehicleStatus struct { VehicleId int64 `json:"vhsId"` // + 9969776690 5198812434 OdometerValue int `json:"odometerValue"` // + 23787 OdometerValueKm int `json:"odometerValueKilometers"` // + 38273 EventDate UnixTime `json:"eventDate"` // + 1701896993000 EventDateStr string `json:"eventDateStr"` // + 2023-12-06T21:09+0000 EventDateCarUser UnixTime `json:"eventDateCarUser"` // + 1701896993000 EventDateStrCarUser string `json:"eventDateStrCarUser"` // + 2023-12-06T21:09+0000 Latitude float64 `json:"latitude"` // + 40.700183 Longitude float64 `json:"longitude"` // + -74.401372 Heading int `json:"positionHeadingDegree,string"` // + "154" DistanceToEmptyFuelMiles float64 `json:"distanceToEmptyFuelMiles"` // + 209.4 DistanceToEmptyFuelKilometers int `json:"distanceToEmptyFuelKilometers"` // + 337 DistanceToEmptyFuelMiles10s int `json:"distanceToEmptyFuelMiles10s"` // + 210 DistanceToEmptyFuelKilometers10s int `json:"distanceToEmptyFuelKilometers10s"` // + 340 AvgFuelConsumptionMpg float64 `json:"avgFuelConsumptionMpg"` // + 18.4 AvgFuelConsumptionLitersPer100Kilometers float64 `json:"avgFuelConsumptionLitersPer100Kilometers"` // + 12.8 RemainingFuelPercent int `json:"remainingFuelPercent"` // + 82 TirePressureFrontLeft int `json:"tirePressureFrontLeft,string,omitempty"` // + "2275" TirePressureFrontRight int `json:"tirePressureFrontRight,string,omitempty"` // + "2344" TirePressureRearLeft int `json:"tirePressureRearLeft,string,omitempty"` // + "2413" TirePressureRearRight int `json:"tirePressureRearRight,string,omitempty"` // + "2344" TirePressureFrontLeftPsi float64 `json:"tirePressureFrontLeftPsi,string,omitempty"` // + "33" TirePressureFrontRightPsi float64 `json:"tirePressureFrontRightPsi,string,omitempty"` // + "34" TirePressureRearLeftPsi float64 `json:"tirePressureRearLeftPsi,string,omitempty"` // + "35" TirePressureRearRightPsi float64 `json:"tirePressureRearRightPsi,string,omitempty"` // + "34" TyreStatusFrontLeft string `json:"tyreStatusFrontLeft"` // + "UNKNOWN" TyreStatusFrontRight string `json:"tyreStatusFrontRight"` // + "UNKNOWN" TyreStatusRearLeft string `json:"tyreStatusRearLeft"` // + "UNKNOWN" TyreStatusRearRight string `json:"tyreStatusRearRight"` // + "UNKNOWN" EvStateOfChargePercent float64 `json:"evStateOfChargePercent,omitempty"` // + null EvDistanceToEmptyMiles int `json:"evDistanceToEmptyMiles,omitempty"` // + null EvDistanceToEmptyKilometers int `json:"evDistanceToEmptyKilometers,omitempty"` // + null EvDistanceToEmptyByStateMiles int `json:"evDistanceToEmptyByStateMiles,omitempty"` // + null EvDistanceToEmptyByStateKilometers int `json:"evDistanceToEmptyByStateKilometers,omitempty"` // + null VehicleStateType string `json:"vehicleStateType"` // + "IGNITION_OFF | IGNITION_ON" WindowFrontLeftStatus string `json:"windowFrontLeftStatus"` // CLOSE | VENTED | OPEN WindowFrontRightStatus string `json:"windowFrontRightStatus"` // CLOSE | VENTED | OPEN WindowRearLeftStatus string `json:"windowRearLeftStatus"` // CLOSE | VENTED | OPEN WindowRearRightStatus string `json:"windowRearRightStatus"` // CLOSE | VENTED | OPEN WindowSunroofStatus string `json:"windowSunroofStatus"` // CLOSE | SLIDE_PARTLY_OPEN | OPEN | TILT DoorBootPosition string `json:"doorBootPosition"` // CLOSED | OPEN DoorEngineHoodPosition string `json:"doorEngineHoodPosition"` // CLOSED | OPEN DoorFrontLeftPosition string `json:"doorFrontLeftPosition"` // CLOSED | OPEN DoorFrontRightPosition string `json:"doorFrontRightPosition"` // CLOSED | OPEN DoorRearLeftPosition string `json:"doorRearLeftPosition"` // CLOSED | OPEN DoorRearRightPosition string `json:"doorRearRightPosition"` // CLOSED | OPEN DoorBootLockStatus string `json:"doorBootLockStatus"` // LOCKED | UNLOCKED DoorFrontLeftLockStatus string `json:"doorFrontLeftLockStatus"` // LOCKED | UNLOCKED DoorFrontRightLockStatus string `json:"doorFrontRightLockStatus"` // LOCKED | UNLOCKED DoorRearLeftLockStatus string `json:"doorRearLeftLockStatus"` // LOCKED | UNLOCKED DoorRearRightLockStatus string `json:"doorRearRightLockStatus"` // LOCKED | UNLOCKED } // VehicleCondition . // "dataName":"remoteServiceStatus" // "remoteServiceType":"condition" type VehicleCondition struct { VehicleStateType string `json:"vehicleStateType"` // "IGNITION_OFF | IGNITION_ON" AvgFuelConsumption float64 `json:"avgFuelConsumption,omitempty"` // null | 18.4 AvgFuelConsumptionUnit string `json:"avgFuelConsumptionUnit"` // "MPG" DistanceToEmptyFuel int `json:"distanceToEmptyFuel,omitempty"` // null | 160 DistanceToEmptyFuelUnit string `json:"distanceToEmptyFuelUnit"` // "MILES" RemainingFuelPercent int `json:"remainingFuelPercent,string"` // "66" Odometer int `json:"odometer"` // 92 OdometerUnit string `json:"odometerUnit"` // "MILES" TirePressureFrontLeft float64 `json:"tirePressureFrontLeft,omitempty"` // null | 36 TirePressureFrontLeftUnit string `json:"tirePressureFrontLeftUnit"` // "PSI" TirePressureFrontRight float64 `json:"tirePressureFrontRight,omitempty"` // null | 36 TirePressureFrontRightUnit string `json:"tirePressureFrontRightUnit"` // "PSI", TirePressureRearLeft float64 `json:"tirePressureRearLeft,omitempty"` // null | 36 TirePressureRearLeftUnit string `json:"tirePressureRearLeftUnit"` // "PSI" TirePressureRearRight float64 `json:"tirePressureRearRight,omitempty"` // null | 36 TirePressureRearRightUnit string `json:"tirePressureRearRightUnit"` // "PSI" DoorBootPosition string `json:"doorBootPosition"` // "CLOSED | OPEN" DoorEngineHoodPosition string `json:"doorEngineHoodPosition"` // "CLOSED | OPEN" DoorFrontLeftPosition string `json:"doorFrontLeftPosition"` // "CLOSED | OPEN" DoorFrontRightPosition string `json:"doorFrontRightPosition"` // "CLOSED | OPEN" DoorRearLeftPosition string `json:"doorRearLeftPosition"` // "CLOSED | OPEN" DoorRearRightPosition string `json:"doorRearRightPosition"` // "CLOSED | OPEN" WindowFrontLeftStatus string `json:"windowFrontLeftStatus"` // "CLOSE | VENTED | OPEN" WindowFrontRightStatus string `json:"windowFrontRightStatus"` // "CLOSE | VENTED | OPEN" WindowRearLeftStatus string `json:"windowRearLeftStatus"` // "CLOSE | VENTED | OPEN" WindowRearRightStatus string `json:"windowRearRightStatus"` // "CLOSE | VENTED | OPEN" WindowSunroofStatus string `json:"windowSunroofStatus"` // "CLOSE | VENTED | OPEN" EvDistanceToEmpty int `json:"evDistanceToEmpty,omitempty"` // null, EvDistanceToEmptyUnit string `json:"evDistanceToEmptyUnit,omitempty"` // null, EvChargerStateType string `json:"evChargerStateType,omitempty"` // null, EvIsPluggedIn bool `json:"evIsPluggedIn,omitempty"` // null, EvStateOfChargeMode string `json:"evStateOfChargeMode,omitempty"` // null, EvTimeToFullyCharged string `json:"evTimeToFullyCharged,omitempty"` // null, EvStateOfChargePercent int `json:"evStateOfChargePercent,omitempty"` // null, LastUpdatedTime string `json:"lastUpdatedTime"` // "2023-04-10T17:50:54+0000", } // ClimateSettings . // "dataName":null // type ClimateSettings struct { // RunTimeMinutes string `json:"runTimeMinutes"` // StartConfiguration string `json:"startConfiguration"` // AirConditionOn string `json:"airConditionOn"` // OuterAirCirculation string `json:"outerAirCirculation"` // ClimateZoneFrontAirMode string `json:"climateZoneFrontAirMode"` // ClimateZoneFrontTemp string `json:"climateZoneFrontTemp"` // ClimateZoneFrontAirVolume string `json:"climateZoneFrontAirVolume"` // HeatedSeatFrontLeft string `json:"heatedSeatFrontLeft"` // HeatedSeatFrontRight string `json:"heatedSeatFrontRight"` // HeatedRearWindowActive string `json:"heatedRearWindowActive"` // } // ServiceRequest . // "dataName": "remoteServiceStatus" type ServiceRequest struct { ServiceRequestID string `json:"serviceRequestId,omitempty"` // 4S4BTGND8L3137058_1640294426029_19_@NGTP Vin string `json:"vin"` // 4S4BTGND8L3137058 Success bool `json:"success"` // false | true // Could be in the false state while the executed request in the progress Cancelled bool `json:"cancelled"` // false | true RemoteServiceType string `json:"remoteServiceType"` // vehicleStatus | condition | locate | unlock | lock | lightsOnly | engineStart | engineStop | phevChargeNow RemoteServiceState string `json:"remoteServiceState"` // started | finished | stopping SubState string `json:"subState,omitempty"` // null ErrorCode string `json:"errorCode,omitempty"` // null:null Result json.RawMessage `json:"result,omitempty"` // struct UpdateTime UnixTime `json:"updateTime,omitempty"` // timestamp // is empty if the request is started } // climateSettings: [ climateSettings ] // climateZoneFrontTempCelsius: [for _ in range(15, 30 + 1)] // climateZoneFrontTemp: [for _ in range(60, 85 + 1)] // climateZoneFrontAirVolume: [ AUTO | 2 | 4 | 7 ] // heatedSeatFrontLeft: [ OFF | LOW_HEAT | MEDIUM_HEAT | HIGH_HEAT ] // heatedSeatFrontRight: [ OFF | LOW_HEAT | MEDIUM_HEAT | HIGH_HEAT ] // climateZoneFrontAirMode: [ WINDOW | FEET_WINDOW | FACE | FEET | FEET_FACE_BALANCED | AUTO ] // outerAirCirculation: [ outsideAir, recirculation ] // airConditionOn: [ false | true ] // heatedRearWindowActive: [ false | true ] // startConfiguration: [ START_CLIMATE_CONTROL_ONLY_ALLOW_KEY_IN_IGNITION | START_ENGINE_ALLOW_KEY_IN_IGNITION ] // runTimeMinutes: [ 10 ] type VehicleHealth struct { VehicleHealthItems []VehicleHealthItem `json:"vehicleHealthItems"` LastUpdatedDate int64 `json:"lastUpdatedDate"` } type VehicleHealthItem struct { B2cCode string `json:"b2cCode"` FeatureCode string `json:"featureCode"` IsTrouble bool `json:"isTrouble"` OnDaiID int `json:"onDaiId"` // Has a number, probably id, but I couldn't find it purpose OnDates []int64 `json:"onDates,omitempty"` // List of the timestamps WarningCode int `json:"warningCode"` } // ErrorResponse . // "dataName":"errorResponse" // {"success":false,"errorCode":"404-soa-unableToParseResponseBody","dataName":"errorResponse","data":{"errorLabel":"404-soa-unableToParseResponseBody","errorDescription":null}} // {"success":false,"errorCode":"vehicleNotInAccount","dataName":null,"data":null} // {"httpCode":500,"errorCode":"error","errorMessage":"java.lang.NullPointerException - null"} // {"success":false,"errorCode":"InvalidCredentials","dataName":"remoteServiceStatus","data":{"serviceRequestId":null,"success":false,"cancelled":false,"remoteServiceType":null,"remoteServiceState":null,"subState":null,"errorCode":null,"result":null,"updateTime":null,"vin":null,"errorDescription":"The credentials supplied are invalid, tries left 2"}} type ErrorResponse struct { ErrorLabel string `json:"errorLabel"` // "404-soa-unableToParseResponseBody" ErrorDescription string `json:"errorDescription,omitempty"` // null } // UnixTime is a wrapper around time.Time that allows us to marshal and unmarshal Unix timestamps type UnixTime struct { time.Time } // UnmarshalJSON is the method that satisfies the Unmarshaller interface // Note that it uses a pointer receiver. It needs this because it will be modifying the embedded time.Time instance func (u *UnixTime) UnmarshalJSON(b []byte) error { var timestamp int64 err := json.Unmarshal(b, ×tamp) if err != nil { return err } u.Time = time.Unix(timestamp, 0) return nil } // MarshalJSON turns our time.Time back into an int func (u UnixTime) MarshalJSON() ([]byte, error) { return []byte(fmt.Sprintf("%d", (u.Time.Unix()))), nil } // CustomTime1 "2021-12-22T13:14:47" is a custom type for unmarshalling time strings type CustomTime1 struct { time.Time } // UnmarshalJSON implements the json.Unmarshaler interface func (ct *CustomTime1) UnmarshalJSON(b []byte) (err error) { // Use the correct layout string for the desired format const layout = "2006-01-02T15:04:05" ct.Time, err = time.Parse(layout, string(b)) return } // CustomTime2 "2023-04-10T17:50:54+0000" is a custom type for unmarshalling time strings type CustomTime2 struct { time.Time } func (ct *CustomTime2) UnmarshalJSON(b []byte) (err error) { const layout = "2006-01-02T15:04:05-0700" ct.Time, err = time.Parse(layout, string(b)) // Parse the string using the custom layout return }