From 2d04e21f35b70f4d493a7582ad742570a1dc94c5 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 13 May 2026 18:43:37 +0000 Subject: [PATCH 1/5] Add missing AchPayment and AchReceivedPayment fields per Unit docs Per https://docs.unit.co/resources: AchPayment: add expectedCompletionDate, counterpartyVerificationMethod, sameDay, secCode. AchReceivedPayment: add isAdvanceable, direction, originatorEntityId, receivingEntityName. Also assign self.id which was previously missing. Co-authored-by: Avery Kushner --- unit/models/payment.py | 52 +++++++++++++++++++++++++++++++----------- 1 file changed, 39 insertions(+), 13 deletions(-) diff --git a/unit/models/payment.py b/unit/models/payment.py index 971b173..12de7ea 100644 --- a/unit/models/payment.py +++ b/unit/models/payment.py @@ -23,20 +23,34 @@ class AchPaymentDTO(BasePayment): def __init__(self, id: str, created_at: datetime, status: PaymentStatus, counterparty: Counterparty, direction: str, description: str, amount: int, addenda: Optional[str], reason: Optional[str], settlement_date: Optional[datetime], tags: Optional[Dict[str, str]], - relationships: Optional[Dict[str, Relationship]]): + relationships: Optional[Dict[str, Relationship]], + expected_completion_date: Optional[datetime] = None, + counterparty_verification_method: Optional[str] = None, + same_day: Optional[bool] = None, sec_code: Optional[str] = None): BasePayment.__init__(self, id, created_at, status, direction, description, amount, reason, tags, relationships) self.type = 'achPayment' self.attributes["counterparty"] = counterparty self.attributes["addenda"] = addenda + self.attributes["expectedCompletionDate"] = expected_completion_date + self.attributes["counterpartyVerificationMethod"] = counterparty_verification_method + self.attributes["sameDay"] = same_day + self.attributes["secCode"] = sec_code self.settlement_date = settlement_date @staticmethod def from_json_api(_id, _type, attributes, relationships): settlement_date = date_utils.to_date(attributes.get("settlementDate")) if attributes.get("settlementDate") else None - return AchPaymentDTO(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["status"], - attributes["counterparty"], attributes["direction"], attributes["description"], - attributes["amount"], attributes.get("addenda"), attributes.get("reason"), settlement_date, - attributes.get("tags"), relationships) + expected_completion_date = date_utils.to_date(attributes.get("expectedCompletionDate")) \ + if attributes.get("expectedCompletionDate") else None + return AchPaymentDTO( + id=_id, created_at=date_utils.to_datetime(attributes["createdAt"]), status=attributes["status"], + counterparty=attributes["counterparty"], direction=attributes["direction"], + description=attributes["description"], amount=attributes["amount"], addenda=attributes.get("addenda"), + reason=attributes.get("reason"), settlement_date=settlement_date, tags=attributes.get("tags"), + relationships=relationships, expected_completion_date=expected_completion_date, + counterparty_verification_method=attributes.get("counterpartyVerificationMethod"), + same_day=attributes.get("sameDay"), sec_code=attributes.get("secCode"), + ) class SimulateIncomingAchPaymentDTO(BasePayment): def __init__(self, id: str, created_at: datetime, status: PaymentStatus, direction: str, @@ -150,11 +164,16 @@ def __init__(self, id: str, created_at: datetime, status: AchReceivedPaymentStat completion_date: datetime, return_reason: Optional[str], amount: int, description: str, addenda: Optional[str], company_name: str, counterparty_routing_number: str, trace_number: str, sec_code: Optional[str], return_cutoff_time: Optional[datetime], can_be_reprocessed: Optional[bool], - tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): + tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]], + is_advanceable: Optional[bool] = None, direction: Optional[str] = None, + originator_entity_id: Optional[str] = None, receiving_entity_name: Optional[str] = None): + self.id = id self.type = "achReceivedPayment" self.attributes = {"createdAt": created_at, "status": status, "wasAdvanced": was_advanced, + "isAdvanceable": is_advanceable, "direction": direction, "completionDate": completion_date, "returnReason": return_reason, "description": description, "amount": amount, "addenda": addenda, "companyName": company_name, + "originatorEntityId": originator_entity_id, "receivingEntityName": receiving_entity_name, "counterpartyRoutingNumber": counterparty_routing_number, "traceNumber": trace_number, "secCode": sec_code, "returnCutoffTime": return_cutoff_time, "canBeReprocessed": can_be_reprocessed, "tags": tags} @@ -162,13 +181,20 @@ def __init__(self, id: str, created_at: datetime, status: AchReceivedPaymentStat @staticmethod def from_json_api(_id, _type, attributes, relationships): - return AchReceivedPaymentDTO(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["status"], - attributes["wasAdvanced"], attributes["completionDate"], - attributes.get("returnReason"),attributes["amount"], attributes["description"], - attributes.get("addenda"), attributes.get("companyName"), - attributes.get("counterpartyRoutingNumber"), attributes.get("traceNumber"), - attributes.get("secCode"), attributes.get("returnCutoffTime"), attributes.get("canBeReprocessed"), - attributes.get("tags"), relationships) + return AchReceivedPaymentDTO( + id=_id, created_at=date_utils.to_datetime(attributes["createdAt"]), status=attributes["status"], + was_advanced=attributes["wasAdvanced"], completion_date=attributes["completionDate"], + return_reason=attributes.get("returnReason"), amount=attributes["amount"], + description=attributes["description"], addenda=attributes.get("addenda"), + company_name=attributes.get("companyName"), + counterparty_routing_number=attributes.get("counterpartyRoutingNumber"), + trace_number=attributes.get("traceNumber"), sec_code=attributes.get("secCode"), + return_cutoff_time=attributes.get("returnCutoffTime"), + can_be_reprocessed=attributes.get("canBeReprocessed"), tags=attributes.get("tags"), + relationships=relationships, is_advanceable=attributes.get("isAdvanceable"), + direction=attributes.get("direction"), originator_entity_id=attributes.get("originatorEntityId"), + receiving_entity_name=attributes.get("receivingEntityName"), + ) class CreatePaymentBaseRequest(UnitRequest): def __init__(self, amount: int, description: str, relationships: Dict[str, Relationship], From 43b30f64824071e9bdb5ff3a40a66dada5b6cbd5 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 13 May 2026 18:43:43 +0000 Subject: [PATCH 2/5] Add missing DepositAccount and AccountEndOfDay fields per Unit docs Per https://docs.unit.co/resources: DepositAccount: add updatedAt, freezeReason, fraudReason, dacaStatus, interestTerms. AccountEndOfDay: add overdraftLimit. Co-authored-by: Avery Kushner --- unit/models/account.py | 23 ++++++++++++++++------- unit/models/account_end_of_day.py | 12 ++++++++---- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/unit/models/account.py b/unit/models/account.py index 41bf255..d5240a5 100644 --- a/unit/models/account.py +++ b/unit/models/account.py @@ -15,22 +15,31 @@ class DepositAccountDTO(object): def __init__(self, id: str, created_at: datetime, name: str, deposit_product: str, routing_number: str, account_number: str, currency: str, balance: int, hold: int, available: int, status: AccountStatus, tags: Optional[Dict[str, str]], close_reason: Optional[CloseReason], - relationships: Optional[Dict[str, Relationship]]): + relationships: Optional[Dict[str, Relationship]], updated_at: Optional[datetime] = None, + freeze_reason: Optional[str] = None, fraud_reason: Optional[FraudReason] = None, + daca_status: Optional[str] = None, interest_terms: Optional[Dict] = None): self.id = id self.type = "depositAccount" - self.attributes = {"name": name, "createdAt": created_at, "depositProduct": deposit_product, + self.attributes = {"name": name, "createdAt": created_at, "updatedAt": updated_at, + "depositProduct": deposit_product, "routingNumber": routing_number, "accountNumber": account_number, "currency": currency, "balance": balance, "hold": hold, "available": available, "status": status, - "closeReason": close_reason, "tags": tags} + "freezeReason": freeze_reason, "closeReason": close_reason, "fraudReason": fraud_reason, + "dacaStatus": daca_status, "interestTerms": interest_terms, "tags": tags} self.relationships = relationships @staticmethod def from_json_api(_id, _type, attributes, relationships): return DepositAccountDTO( - _id, date_utils.to_datetime(attributes["createdAt"]), attributes["name"], attributes["depositProduct"], - attributes["routingNumber"], attributes["accountNumber"], attributes["currency"], attributes["balance"], - attributes["hold"], attributes["available"], attributes["status"], attributes.get("tags"), - attributes.get("closeReason"), relationships + id=_id, created_at=date_utils.to_datetime(attributes["createdAt"]), name=attributes["name"], + deposit_product=attributes["depositProduct"], routing_number=attributes["routingNumber"], + account_number=attributes["accountNumber"], currency=attributes["currency"], + balance=attributes["balance"], hold=attributes["hold"], available=attributes["available"], + status=attributes["status"], tags=attributes.get("tags"), + close_reason=attributes.get("closeReason"), relationships=relationships, + updated_at=date_utils.to_datetime(attributes.get("updatedAt")) if attributes.get("updatedAt") else None, + freeze_reason=attributes.get("freezeReason"), fraud_reason=attributes.get("fraudReason"), + daca_status=attributes.get("dacaStatus"), interest_terms=attributes.get("interestTerms"), ) diff --git a/unit/models/account_end_of_day.py b/unit/models/account_end_of_day.py index b22bf22..e6c5bf6 100644 --- a/unit/models/account_end_of_day.py +++ b/unit/models/account_end_of_day.py @@ -5,16 +5,20 @@ class AccountEndOfDayDTO(object): def __init__(self, id: str, date: str, balance: int, hold: int, available: int, - relationships: Optional[Dict[str, Relationship]]): + relationships: Optional[Dict[str, Relationship]], overdraft_limit: Optional[int] = None): self.id = id self.type = "accountEndOfDay" - self.attributes = {"date": date, "balance": balance, "hold": hold, "available": available} + self.attributes = {"date": date, "balance": balance, "hold": hold, "available": available, + "overdraftLimit": overdraft_limit} self.relationships = relationships @staticmethod def from_json_api(_id, _type, attributes, relationships): - return AccountEndOfDayDTO(_id, attributes["date"], attributes["balance"], attributes["hold"], - attributes["available"], relationships) + return AccountEndOfDayDTO( + id=_id, date=attributes["date"], balance=attributes["balance"], hold=attributes["hold"], + available=attributes["available"], relationships=relationships, + overdraft_limit=attributes.get("overdraftLimit"), + ) class ListAccountEndOfDayParams(UnitParams): From eebc3e6570648dfb96704ec9cdfa032380964a22 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 13 May 2026 18:43:50 +0000 Subject: [PATCH 3/5] Add missing IndividualCustomer and BusinessCustomer fields per Unit docs Per https://docs.unit.co/resources: IndividualCustomer: add eligibleProducts (Array of Product) and ein (Optional, sole-prop with EIN). BusinessCustomer: add eligibleProducts. Co-authored-by: Avery Kushner --- unit/models/customer.py | 46 ++++++++++++++++++++++++++--------------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/unit/models/customer.py b/unit/models/customer.py index d1ceab7..c5e0c7a 100644 --- a/unit/models/customer.py +++ b/unit/models/customer.py @@ -11,24 +11,31 @@ def __init__(self, id: str, created_at: datetime, full_name: FullName, date_of_b phone: Phone, email: str, ssn: Optional[str], passport: Optional[str], nationality: Optional[str], authorized_users: [AuthorizedUser], tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]], status: CustomerStatus, - archive_reason: Optional[ArchiveReason]): + archive_reason: Optional[ArchiveReason], eligible_products: Optional[List[str]] = None, + ein: Optional[str] = None): self.id = id self.type = 'individualCustomer' self.attributes = {"createdAt": created_at, "fullName": full_name, "dateOfBirth": date_of_birth, "address": address, "phone": phone, "email": email, "ssn": ssn, "passport": passport, - "nationality": nationality, "authorizedUsers": authorized_users, "tags": tags, + "nationality": nationality, "ein": ein, "authorizedUsers": authorized_users, + "eligibleProducts": eligible_products, "tags": tags, "status": status, "archiveReason": archive_reason} self.relationships = relationships @staticmethod def from_json_api(_id, _type, attributes, relationships): return IndividualCustomerDTO( - _id, date_utils.to_datetime(attributes["createdAt"]), - FullName.from_json_api(attributes["fullName"]), date_utils.to_date(attributes["dateOfBirth"]), - Address.from_json_api(attributes["address"]), Phone.from_json_api(attributes["phone"]), - attributes["email"], attributes.get("ssn"), attributes.get("passport"), attributes.get("nationality"), - AuthorizedUser.from_json_api(attributes["authorizedUsers"]), attributes.get("tags"), relationships, - attributes.get("status"), attributes.get("archiveReason") + id=_id, created_at=date_utils.to_datetime(attributes["createdAt"]), + full_name=FullName.from_json_api(attributes["fullName"]), + date_of_birth=date_utils.to_date(attributes["dateOfBirth"]), + address=Address.from_json_api(attributes["address"]), + phone=Phone.from_json_api(attributes["phone"]), + email=attributes["email"], ssn=attributes.get("ssn"), passport=attributes.get("passport"), + nationality=attributes.get("nationality"), + authorized_users=AuthorizedUser.from_json_api(attributes["authorizedUsers"]), + tags=attributes.get("tags"), relationships=relationships, status=attributes.get("status"), + archive_reason=attributes.get("archiveReason"), + eligible_products=attributes.get("eligibleProducts"), ein=attributes.get("ein"), ) @@ -37,25 +44,30 @@ def __init__(self, id: str, created_at: datetime, name: str, address: Address, p state_of_incorporation: str, ein: str, entity_type: EntityType, contact: BusinessContact, authorized_users: [AuthorizedUser], dba: Optional[str], tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]], status: CustomerStatus, - archive_reason: Optional[ArchiveReason]): + archive_reason: Optional[ArchiveReason], eligible_products: Optional[List[str]] = None): self.id = id self.type = 'businessCustomer' self.attributes = {"createdAt": created_at, "name": name, "address": address, "phone": phone, "stateOfIncorporation": state_of_incorporation, "ein": ein, "entityType": entity_type, - "contact": contact, "authorizedUsers": authorized_users, "dba": dba, "tags": tags, + "contact": contact, "authorizedUsers": authorized_users, "dba": dba, + "eligibleProducts": eligible_products, "tags": tags, "status": status, "archiveReason": archive_reason} self.relationships = relationships @staticmethod def from_json_api(_id, _type, attributes, relationships): return BusinessCustomerDTO( - _id, date_utils.to_datetime(attributes["createdAt"]), attributes["name"], - Address.from_json_api(attributes["address"]), Phone.from_json_api(attributes["phone"]), - attributes["stateOfIncorporation"], attributes["ein"], attributes["entityType"], - BusinessContact.from_json_api(attributes["contact"]), - AuthorizedUser.from_json_api(attributes["authorizedUsers"]), - attributes.get("dba"), attributes.get("tags"), relationships, attributes.get("status"), - attributes.get("archiveReason")) + id=_id, created_at=date_utils.to_datetime(attributes["createdAt"]), name=attributes["name"], + address=Address.from_json_api(attributes["address"]), + phone=Phone.from_json_api(attributes["phone"]), + state_of_incorporation=attributes["stateOfIncorporation"], ein=attributes["ein"], + entity_type=attributes["entityType"], + contact=BusinessContact.from_json_api(attributes["contact"]), + authorized_users=AuthorizedUser.from_json_api(attributes["authorizedUsers"]), + dba=attributes.get("dba"), tags=attributes.get("tags"), relationships=relationships, + status=attributes.get("status"), archive_reason=attributes.get("archiveReason"), + eligible_products=attributes.get("eligibleProducts"), + ) CustomerDTO = Union[IndividualCustomerDTO, BusinessCustomerDTO] From bfd0f7e60a98f4baa94496653697c75fa544525a Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 13 May 2026 18:43:55 +0000 Subject: [PATCH 4/5] Add missing CounterpartyDTO tags field per Unit docs Per https://docs.unit.co/resources, ACH counterparties expose a tags attribute that the DTO was not deserializing. Co-authored-by: Avery Kushner --- unit/models/counterparty.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/unit/models/counterparty.py b/unit/models/counterparty.py index 987c4ba..01415a1 100644 --- a/unit/models/counterparty.py +++ b/unit/models/counterparty.py @@ -8,19 +8,23 @@ class CounterpartyDTO(object): def __init__(self, id: str, created_at: datetime, name: str, routing_number: str, bank: Optional[str], account_number: str, account_type: str, type: str, permissions: str, - relationships: [Dict[str, Relationship]]): + relationships: [Dict[str, Relationship]], tags: Optional[Dict[str, str]] = None): self.id = id self.type = "achCounterparty" self.attributes = {"createdAt": created_at, "name": name, "routingNumber": routing_number, "bank": bank, "accountNumber": account_number, "accountType": account_type, "type": type, - "permissions": permissions} + "permissions": permissions, "tags": tags} self.relationships = relationships @staticmethod def from_json_api(_id, _type, attributes, relationships): - return CounterpartyDTO(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["name"], - attributes["routingNumber"], attributes.get("bank"), attributes["accountNumber"], - attributes["accountType"], attributes["type"], attributes["permissions"], relationships) + return CounterpartyDTO( + id=_id, created_at=date_utils.to_datetime(attributes["createdAt"]), name=attributes["name"], + routing_number=attributes["routingNumber"], bank=attributes.get("bank"), + account_number=attributes["accountNumber"], account_type=attributes["accountType"], + type=attributes["type"], permissions=attributes["permissions"], relationships=relationships, + tags=attributes.get("tags"), + ) class CreateCounterpartyRequest(UnitRequest): From 4eab9a2eda8a5400ba82a4e45be090636f443735 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 13 May 2026 18:44:03 +0000 Subject: [PATCH 5/5] Add DisputeDTO updatedAt field and fix from_json_api kwargs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per https://docs.unit.co/resources, Dispute exposes an optional updatedAt attribute. Also switch from_json_api to use keyword arguments — the previous positional call assigned attributes to the wrong constructor parameters (createdAt was being passed as 'source', etc.) but happened to work since values were stored in self.attributes by name in __init__. Co-authored-by: Avery Kushner --- unit/models/dispute.py | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/unit/models/dispute.py b/unit/models/dispute.py index ca3f6fb..0b7bdfe 100644 --- a/unit/models/dispute.py +++ b/unit/models/dispute.py @@ -18,16 +18,18 @@ def __init__( status_history: List[Dict[str, str]], status: DisputeStatus, description: str, - dispute_type: str, + dispute_type: Optional[str], created_at: datetime, amount: int, decision_reason: Optional[str], relationships: Optional[Dict[str, Relationship]], + updated_at: Optional[datetime] = None, ): self.id = id self.type = "dispute" self.attributes = { "createdAt": created_at, + "updatedAt": updated_at, "source": source, "statusHistory": status_history, "status": status, @@ -41,14 +43,15 @@ def __init__( @staticmethod def from_json_api(_id, _type, attributes, relationships): return DisputeDTO( - _id, - date_utils.to_datetime(attributes["createdAt"]), - attributes["source"], - attributes["statusHistory"], - attributes["status"], - attributes["description"], - attributes["disputeType"], - attributes["amount"], - attributes.get("decisionReason"), - relationships, + id=_id, + source=attributes["source"], + status_history=attributes["statusHistory"], + status=attributes["status"], + description=attributes["description"], + dispute_type=attributes.get("disputeType"), + created_at=date_utils.to_datetime(attributes["createdAt"]), + amount=attributes["amount"], + decision_reason=attributes.get("decisionReason"), + relationships=relationships, + updated_at=date_utils.to_datetime(attributes.get("updatedAt")) if attributes.get("updatedAt") else None, )