Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions cuenca/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
'LimitedWallet',
'LoginToken',
'Otp',
'PasswordReset',
'Platform',
'Questionnaires',
'Saving',
Expand Down Expand Up @@ -76,6 +77,7 @@
LimitedWallet,
LoginToken,
Otp,
PasswordReset,
PhoneVerificationAssociations,
Platform,
PostalCodes,
Expand Down
2 changes: 1 addition & 1 deletion cuenca/http/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from ..version import API_VERSION, CLIENT_VERSION

API_HOST = 'api.cuenca.com'
SANDBOX_HOST = 'sandbox.cuenca.com'
SANDBOX_HOST = 'api.cuenca.com'
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Restore a distinct sandbox host for sandbox mode.

Line 21 makes SANDBOX_HOST equal to API_HOST, so configure(sandbox=True) no longer changes environment. This collapses sandbox/prod separation and matches the observed 403 failures in password reset tests.

🔧 Proposed fix
 API_HOST = 'api.cuenca.com'
-SANDBOX_HOST = 'api.cuenca.com'
+SANDBOX_HOST = 'sandbox.cuenca.com'
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cuenca/http/client.py` at line 21, SANDBOX_HOST is incorrectly set to the
same value as API_HOST so configure(sandbox=True) doesn't switch environments;
restore a distinct sandbox hostname by changing the SANDBOX_HOST assignment
(symbol SANDBOX_HOST in cuenca/http/client.py) back to the sandbox-specific host
used by the service and ensure configure(sandbox=True) uses SANDBOX_HOST instead
of API_HOST (refer to the configure(sandbox=True) logic and any host-selection
code that reads API_HOST/SANDBOX_HOST).



class Session:
Expand Down
3 changes: 3 additions & 0 deletions cuenca/resources/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
'LimitedWallet',
'LoginToken',
'Otp',
'PasswordReset',
'Platform',
'PhoneVerificationAssociation',
'Questionnaires',
Expand Down Expand Up @@ -70,6 +71,7 @@
from .limited_wallets import LimitedWallet
from .login_tokens import LoginToken
from .otps import Otp
from .password_resets import PasswordReset
from .phone_verification_associations import PhoneVerificationAssociations
from .platforms import Platform
from .postal_codes import PostalCodes
Expand Down Expand Up @@ -116,6 +118,7 @@
KYCValidation,
LimitedWallet,
LoginToken,
PasswordReset,
Questionnaires,
Saving,
Session,
Expand Down
54 changes: 54 additions & 0 deletions cuenca/resources/password_resets.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import datetime as dt
from typing import ClassVar, Optional

from cuenca_validations.types import VerificationStatus
from cuenca_validations.types.requests import PasswordResetRequest
from pydantic import ConfigDict
from pydantic_extra_types.coordinate import Coordinate

from ..http import Session, session as global_session
from .base import Creatable, Queryable, Retrievable


class PasswordReset(Creatable, Retrievable, Queryable):
_resource: ClassVar = 'password_resets'

platform_id: str
verification_id: str
flow_id: str
status: VerificationStatus = VerificationStatus.created
mati_verification_id: Optional[str] = None
identity_id: Optional[str] = None
provider_url: Optional[str] = None
created_at: dt.datetime
updated_at: Optional[dt.datetime] = None
deactivated_at: Optional[dt.datetime] = None

model_config = ConfigDict(
json_schema_extra={
'example': {
'id': 'PRNEUInh69SuKXXmK95sROwQ',
'platform_id': 'PT-1234567890',
'verification_id': 'VENEUInh69SuKXXmK95sROwQ',
'flow_id': '123e4567-e89b-12d3-a456-426614174000',
'status': 'created',
'mati_verification_id': 'metamap-verification-id',
'identity_id': 'metamap-identity-id',
'created_at': '2026-05-06T14:15:22Z',
}
}
)

@classmethod
def create(
cls,
verification_id: str,
location: Coordinate,
*,
session: Session = global_session,
) -> 'PasswordReset':
req = PasswordResetRequest(
verification_id=verification_id,
location=location,
)
return cls._create(session=session, **req.model_dump())
2 changes: 1 addition & 1 deletion cuenca/version.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
__version__ = '2.1.19'
__version__ = '2.1.20.dev0'
CLIENT_VERSION = __version__
API_VERSION = '2020-03-19'
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
requests==2.32.3
cuenca-validations==2.1.30
cuenca-validations==2.1.31.dev0
pydantic-extra-types==2.10.2
29 changes: 29 additions & 0 deletions tests/resources/test_password_resets.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import pytest
from pydantic_extra_types.coordinate import Coordinate, Latitude, Longitude

from cuenca import PasswordReset


@pytest.mark.vcr
def test_password_resets_create() -> None:
password_reset = PasswordReset.create(
verification_id='VEze_Bh1zhROKehtBOS7EHlw',
location=Coordinate(
latitude=Latitude(19.432608),
longitude=Longitude(-99.133209),
),
)
assert password_reset.id.startswith('PR')


@pytest.mark.vcr
def test_password_resets_retrieve() -> None:
password_reset = PasswordReset.retrieve('PRFOO')
assert password_reset.id
assert password_reset.verification_id


@pytest.mark.vcr
def test_password_resets_all() -> None:
items = list(PasswordReset.all())
assert items
Loading