├── .github └── workflows │ ├── black.yml │ ├── release-pypi.yml │ ├── test.yml │ └── update-docs.yml ├── .gitignore ├── CONTRIBUTORS.md ├── LICENSE ├── NEWS.md ├── Pipfile ├── README.md ├── pyproject.toml ├── requirements.txt ├── setup.cfg ├── setup.py ├── tests ├── _documents │ ├── test_line_items.py │ └── test_tags.py ├── assets │ └── receipt_public.jpg ├── test_a_docs.py ├── test_bank_statements.py ├── test_bussines_cards.py ├── test_checks.py ├── test_documents.py ├── test_errors.py ├── test_w2s.py ├── test_w8s.py └── test_w9s.py ├── tox.ini └── veryfi ├── __init__.py ├── _documents ├── __init__.py ├── line_items.py ├── pdf_split.py └── tags.py ├── _w2s ├── __init__.py └── w2_split.py ├── a_docs.py ├── bank_statements.py ├── bussines_cards.py ├── checks.py ├── client.py ├── client_base.py ├── documents.py ├── errors.py ├── w2s.py ├── w8s.py └── w9s.py /.github/workflows/black.yml: -------------------------------------------------------------------------------- 1 | name: Black 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | lint: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v2 10 | - uses: actions/setup-python@v2 11 | - uses: psf/black@stable 12 | -------------------------------------------------------------------------------- /.github/workflows/release-pypi.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | release: 5 | types: [released] 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-20.04 10 | strategy: 11 | matrix: 12 | python-version: [3.9] 13 | steps: 14 | - uses: actions/checkout@v2 15 | - name: Set up Python ${{ matrix.python-version }} 16 | uses: actions/setup-python@v2 17 | with: 18 | python-version: ${{ matrix.python-version }} 19 | - name: Install dependencies 20 | run: | 21 | python -m pip install --upgrade pip 22 | pip install tox tox-gh-actions 23 | - name: Build package 24 | env: 25 | SKIP_GENERATE_AUTHORS: 1 26 | run: | 27 | pip install -U setuptools wheel pbr 28 | python setup.py sdist bdist_wheel 29 | - name: Publish package 30 | uses: pypa/gh-action-pypi-publish@release/v1 31 | with: 32 | skip_existing: true 33 | user: __token__ 34 | password: ${{ secrets.PYPI }} 35 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | strategy: 9 | matrix: 10 | python-version: [3.9, '3.10', 3.11, 3.12] 11 | steps: 12 | - uses: actions/checkout@v2 13 | - name: Set up Python ${{ matrix.python-version }} 14 | uses: actions/setup-python@v2 15 | with: 16 | python-version: ${{ matrix.python-version }} 17 | - name: Install dependencies 18 | run: | 19 | python -m pip install --upgrade pip 20 | pip install tox tox-gh-actions 21 | - name: Test with tox 22 | run: tox 23 | - name: Build package 24 | env: 25 | SKIP_GENERATE_AUTHORS: 1 26 | run: | 27 | pip install -U setuptools wheel pbr 28 | python setup.py sdist bdist_wheel 29 | -------------------------------------------------------------------------------- /.github/workflows/update-docs.yml: -------------------------------------------------------------------------------- 1 | name: Update docs 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | strategy: 12 | matrix: 13 | python-version: [3.9] 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: Set up Python ${{ matrix.python-version }} 17 | uses: actions/setup-python@v2 18 | with: 19 | python-version: ${{ matrix.python-version }} 20 | - name: Update docs 21 | run: | 22 | mkdir docs 23 | pip install portray 24 | pip install -r requirements.txt 25 | portray on_github_pages -f 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | .pybuilder/ 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | # For a library or package, you might want to ignore these files since the code is 87 | # intended to run in multiple environments; otherwise, check them in: 88 | # .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # UV 98 | # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control. 99 | # This is especially recommended for binary packages to ensure reproducibility, and is more 100 | # commonly ignored for libraries. 101 | #uv.lock 102 | 103 | # poetry 104 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 105 | # This is especially recommended for binary packages to ensure reproducibility, and is more 106 | # commonly ignored for libraries. 107 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 108 | #poetry.lock 109 | 110 | # pdm 111 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 112 | #pdm.lock 113 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 114 | # in version control. 115 | # https://pdm.fming.dev/latest/usage/project/#working-with-version-control 116 | .pdm.toml 117 | .pdm-python 118 | .pdm-build/ 119 | 120 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 121 | __pypackages__/ 122 | 123 | # Celery stuff 124 | celerybeat-schedule 125 | celerybeat.pid 126 | 127 | # SageMath parsed files 128 | *.sage.py 129 | 130 | # Environments 131 | .env 132 | .venv 133 | env/ 134 | venv/ 135 | ENV/ 136 | env.bak/ 137 | venv.bak/ 138 | 139 | # Spyder project settings 140 | .spyderproject 141 | .spyproject 142 | 143 | # Rope project settings 144 | .ropeproject 145 | 146 | # mkdocs documentation 147 | /site 148 | 149 | # mypy 150 | .mypy_cache/ 151 | .dmypy.json 152 | dmypy.json 153 | 154 | # Pyre type checker 155 | .pyre/ 156 | 157 | # pytype static type analyzer 158 | .pytype/ 159 | 160 | # Cython debug symbols 161 | cython_debug/ 162 | 163 | # PyCharm 164 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 165 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 166 | # and can be added to the global gitignore or merged into this file. For a more nuclear 167 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 168 | .idea/ 169 | 170 | # VSCode 171 | .vscode/ -------------------------------------------------------------------------------- /CONTRIBUTORS.md: -------------------------------------------------------------------------------- 1 | # Release process 2 | 3 | Release and upload to pypi is set up in github actions. For that, simply create a release here https://github.com/veryfi/veryfi-python/releases 4 | 5 | ## Versioning 6 | 7 | pbr manages all the package metadata: 8 | * it uses requirements.txt 9 | * it adds version 10 | 11 | To check the package build, simply run `python setup.py sdist bdist_wheel` -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2019 Veryfi, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /NEWS.md: -------------------------------------------------------------------------------- 1 | CHANGES 2 | ======= 3 | 3.4.1 4 | ----- 5 | * Add support to add multiple tags on existing documents 6 | 7 | 3.4.0 8 | ----- 9 | * Add support to add tags on existing documents 10 | 11 | 3.1.1 12 | ----- 13 | * Install dependencies in update-docs workflow 14 | 15 | 3.1.0 16 | ----- 17 | * Add support for operations with line items 18 | 19 | 3.0.0 20 | ----- 21 | * Return proper 404 and other errors 22 | 23 | 3.0.0 24 | ----- 25 | * Use v8 by default, lower timeout 26 | 27 | 2.1.0 28 | ----- 29 | * BREAKING - Remove process\_document\_file 30 | * Fix docs 31 | 32 | 2.0.0 33 | ----- 34 | 35 | * Make username and apikey required, pass kwargs 36 | * Add tests 37 | * Add tox-gh-actions 38 | * Let tox manage python versions 39 | * Feature/updates gitignore (#24) 40 | * Remove MAX\_FILE\_SIZE\_MB 41 | * Update README.md 42 | 43 | 1.1.1 44 | ----- 45 | 46 | * Unpin requests 47 | * Update python-package.yml 48 | * Update README.md 49 | * Force push fresh autodocs to github pages 50 | 51 | 1.1.0 52 | ----- 53 | 54 | * Add update\_document 55 | * Add portray autodocs 56 | * Fix code 57 | 58 | 59 | 1.0.0 60 | ----- 61 | 62 | * Clean test deps 63 | * Update README.md 64 | 65 | 0.0.7 66 | ----- 67 | 68 | * Fix publish package condition 69 | * Add a note on release 70 | * Fix description, remove authors generation 71 | 72 | 73 | 0.0.6 74 | ----- 75 | 76 | * Add missing parameters to process\_document\_url 77 | * Add boost 78 | * Make it black (#4) 79 | * Clean python versions 80 | * Bump to 0.0.5 81 | * Remove python 3.5 support 82 | * Add responses 83 | * Clean setup 84 | * Add test 85 | * Fix file\_url parameter, add Accept header 86 | * Better message in exceptions -------------------------------------------------------------------------------- /Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | name = "pypi" 3 | url = "https://pypi.org/simple" 4 | verify_ssl = true 5 | 6 | [dev-packages] 7 | black = "*" 8 | jupyterlab = "*" 9 | pbr = "*" 10 | portray = "*" 11 | pytest = "*" 12 | responses = "*" 13 | tox = "*" 14 | 15 | [packages] 16 | requests = "*" 17 | 18 | [requires] 19 | python_version = "3.9" 20 | 21 | [pipenv] 22 | allow_prereleases = true 23 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.black] 2 | line-length = 100 3 | target-version = ["py39"] 4 | 5 | [tool.portray] 6 | modules = ["veryfi"] 7 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | requests>=2.22.0 2 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | name = veryfi 3 | description-file = README.md 4 | long_description_content_type = text/markdown 5 | author = Veryfi, Inc. 6 | author_email = support@veryfi.com 7 | url = https://github.com/veryfi/veryfi-python 8 | license = MIT 9 | classifiers = 10 | Development Status :: 5 - Production/Stable 11 | Programming Language :: Python :: 3.9 12 | Programming Language :: Python :: 3.10 13 | Programming Language :: Python :: 3.11 14 | Programming Language :: Python :: 3.12 15 | Programming Language :: Python :: Implementation :: CPython 16 | keywords = veryfi, veryfi.com, ocr api 17 | 18 | 19 | [options] 20 | packages = find: 21 | 22 | 23 | [entry_points] 24 | pbr.config.drivers = 25 | plain = pbr.cfg.driver:Plain 26 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | setup( 4 | setup_requires=["pbr>=1.9", "setuptools>=17.1"], 5 | pbr=True, 6 | ) 7 | -------------------------------------------------------------------------------- /tests/_documents/test_line_items.py: -------------------------------------------------------------------------------- 1 | import responses 2 | 3 | from veryfi import Client 4 | 5 | 6 | @responses.activate 7 | def test_line_items(): 8 | mock_doc_id = 1 9 | mock_line_item_id = 1 10 | mock_resp = { 11 | "line_items": [ 12 | { 13 | "date": "", 14 | "description": "foo", 15 | "discount": 0.0, 16 | "id": mock_line_item_id, 17 | "order": 1, 18 | "price": 0.0, 19 | "quantity": 1.0, 20 | "reference": "", 21 | "sku": "", 22 | "tax": 0.0, 23 | "tax_rate": 0.0, 24 | "total": 1.0, 25 | "type": "food", 26 | "unit_of_measure": "", 27 | } 28 | ], 29 | } 30 | client = Client(client_id="v", client_secret=None, username="o", api_key="c") 31 | responses.add( 32 | responses.GET, 33 | f"{client.versioned_url}/partner/documents/{mock_doc_id}/line-items/", 34 | json=mock_resp, 35 | status=200, 36 | ) 37 | assert client.get_line_items(mock_doc_id) == mock_resp 38 | 39 | responses.add( 40 | responses.GET, 41 | f"{client.versioned_url}/partner/documents/{mock_doc_id}/line-items/{mock_line_item_id}", 42 | json=mock_resp["line_items"][0], 43 | status=200, 44 | ) 45 | assert client.get_line_item(mock_doc_id, mock_line_item_id) == mock_resp["line_items"][0] 46 | 47 | mock_resp["line_items"][0]["description"] = "bar" 48 | responses.add( 49 | responses.PUT, 50 | f"{client.versioned_url}/partner/documents/{mock_doc_id}/line-items/{mock_line_item_id}", 51 | json=mock_resp["line_items"][0], 52 | status=200, 53 | ) 54 | assert ( 55 | client.update_line_item(mock_doc_id, mock_line_item_id, {"description": "foo"}) 56 | == mock_resp["line_items"][0] 57 | ) 58 | 59 | responses.add( 60 | responses.DELETE, 61 | f"{client.versioned_url}/partner/documents/{mock_doc_id}/line-items/{mock_line_item_id}", 62 | json={}, 63 | status=200, 64 | ) 65 | assert client.delete_line_item(mock_doc_id, mock_line_item_id) is None 66 | 67 | responses.add( 68 | responses.DELETE, 69 | f"{client.versioned_url}/partner/documents/{mock_doc_id}/line-items/", 70 | json={}, 71 | status=200, 72 | ) 73 | assert client.delete_line_items(mock_doc_id) is None 74 | 75 | responses.add( 76 | responses.POST, 77 | f"{client.versioned_url}/partner/documents/{mock_doc_id}/line-items/", 78 | json=mock_resp["line_items"][0], 79 | status=200, 80 | ) 81 | 82 | assert ( 83 | client.add_line_item(mock_doc_id, {"order": 1, "description": "foo", "total": 1.0}) 84 | == mock_resp["line_items"][0] 85 | ) 86 | -------------------------------------------------------------------------------- /tests/_documents/test_tags.py: -------------------------------------------------------------------------------- 1 | import responses 2 | 3 | from veryfi import Client 4 | 5 | 6 | @responses.activate 7 | def test_tags(): 8 | mock_doc_id = 169985445 9 | mock_resp = {"id": 6673474, "name": "tag_123"} 10 | client = Client(client_id="v", client_secret="w", username="o", api_key="c") 11 | responses.put( 12 | f"{client.versioned_url}/partner/documents/{mock_doc_id}/tags/", 13 | json=mock_resp, 14 | status=200, 15 | ) 16 | d = client.add_tag(mock_doc_id, "tag_123") 17 | assert d == mock_resp 18 | 19 | 20 | @responses.activate 21 | def test_replace_multiple_tags(): 22 | mock_doc_id = 169985445 23 | mock_resp = {"id": 6673474, "tags": ["tag_1", "tag_2", "tag_3"]} 24 | client = Client(client_id="v", client_secret="w", username="o", api_key="c") 25 | responses.put( 26 | f"{client.versioned_url}/partner/documents/{mock_doc_id}/", 27 | json=mock_resp, 28 | status=200, 29 | ) 30 | d = client.replace_tags(mock_doc_id, ["tag_1", "tag_2", "tag_3"]) 31 | assert d == mock_resp 32 | 33 | 34 | @responses.activate 35 | def test_add_multiple_tags(): 36 | mock_doc_id = 169985445 37 | mock_resp = {"id": 6673474, "tags": ["tag_1", "tag_2", "tag_3"]} 38 | client = Client(client_id="v", client_secret="w", username="o", api_key="c") 39 | responses.post( 40 | f"{client.versioned_url}/partner/documents/{mock_doc_id}/tags/", 41 | json=mock_resp, 42 | status=200, 43 | ) 44 | d = client.add_tags(mock_doc_id, ["tag_1", "tag_2", "tag_3"]) 45 | assert d == mock_resp 46 | -------------------------------------------------------------------------------- /tests/assets/receipt_public.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veryfi/veryfi-python/ebe324aa2471bdac607417f07e7c5d8a779cd201/tests/assets/receipt_public.jpg -------------------------------------------------------------------------------- /tests/test_a_docs.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | import responses 3 | 4 | from veryfi import Client 5 | 6 | 7 | @responses.activate 8 | def test_process_a_doc(): 9 | mock = { 10 | "pdf_url": "https://scdn.veryfi.com/other-documents/919ba4778c039560/cf1363b8-a38f-47e8-b9ee-8105342121cd/7179c430-eb38-4251-b015-9ceb20129371.pdf?Expires=1727203608&Signature=ZSfZmJRLtJ6DeIRioIQSExufnR4fDvADq1Fs-x~WnbU1JueQ1PLtY~7b~Krk7eda6EAQkMBa2wamDDcE2lCvrutHCS3jUbhlFFhSuQd1XljbYjBlWOdxYyXpYMmluDlaWlkgm41vA92UD3LSsBPBLrBasotjqNYLGnTg87guXTtUG1rSWlK2FhHxzborReNdrpXUcDMs4-kkQ46tTDgFH~mCPkh5F9DSpm-UsyJ6SmJgm1SWfw09KbQizyp4lIwte1yumKXtORtTCKv5WFWRUFUWD6Kv1eIkh5XJ5jfMzSfaTEikZlYF4t08Lbp5Apk5-alOW-1yYIwqb5RqZhQ26w__&Key-Pair-Id=APKAJCILBXEJFZF4DCHQ", 11 | "id": 4559535, 12 | "external_id": None, 13 | "created_date": "2024-09-24 18:31:48", 14 | "updated_date": "2024-09-24 18:31:48", 15 | "img_thumbnail_url": "https://scdn.veryfi.com/other-documents/919ba4778c039560/cf1363b8-a38f-47e8-b9ee-8105342121cd/thumbnail.png?Expires=1727203608&Signature=SIRru1E-r1VT5KmufOC9A3UXlWzpgaZWUn0GhSj~veGagGAISV7sztEA7bER~kZlVnowRBSu19UaR8VeGfQ39uzUxEVlzdxPgjITt7IEgfGa~B-0EUI8izLDfRoOMkdRrOknLJKpCq87hz8fMn6wfKSgWxGgyCFKuvO2zcdla~fmtcTOrR4OMAPA3TX4Y4ZRnwCfUDQwNMw72Zihh9bxulzgjM6Cqffc7wta6wC84rYRlztPgGQj51ARcewG5s-IouvrJKoTAONLJZaq8CEc-iMh~TRzKf4MiI5HoheBFmjKb2NdoJFDpHR~~aLW8RxWkEV87JtglILAumkjrY7jjw__&Key-Pair-Id=APKAJCILBXEJFZF4DCHQ", 16 | "blueprint_name": "us_driver_license", 17 | "template_name": "us_driver_license", 18 | "address": "892 MOMONA ST HONOLULU, HI 96820", 19 | "birth_date": "1981-06-03", 20 | "expiration_date": "2008-06-03", 21 | "eyes_color": "BRO", 22 | "first_name": None, 23 | "height": "5-10", 24 | "issue_date": "1998-06-18", 25 | "last_name": "McLovin", 26 | "license_class": "3", 27 | "license_number": "01-47-87441", 28 | "sex": "M", 29 | "state": "HAWAII", 30 | "weight": "150", 31 | } 32 | client = Client(client_id="v", client_secret="w", username="o", api_key="c") 33 | responses.add( 34 | responses.POST, 35 | f"{client.versioned_url}/partner/any-documents/", 36 | json=mock, 37 | status=200, 38 | ) 39 | d = client.process_any_document( 40 | blueprint_name="us_driver_license", 41 | file_path="tests/assets/receipt_public.jpg", 42 | delete_after_processing=True, 43 | boost_mode=True, 44 | ) 45 | assert d == mock 46 | 47 | 48 | @responses.activate 49 | def test_process_document_url(): 50 | mock = { 51 | "pdf_url": "https://scdn.veryfi.com/other-documents/919ba4778c039560/cf1363b8-a38f-47e8-b9ee-8105342121cd/7179c430-eb38-4251-b015-9ceb20129371.pdf?Expires=1727203608&Signature=ZSfZmJRLtJ6DeIRioIQSExufnR4fDvADq1Fs-x~WnbU1JueQ1PLtY~7b~Krk7eda6EAQkMBa2wamDDcE2lCvrutHCS3jUbhlFFhSuQd1XljbYjBlWOdxYyXpYMmluDlaWlkgm41vA92UD3LSsBPBLrBasotjqNYLGnTg87guXTtUG1rSWlK2FhHxzborReNdrpXUcDMs4-kkQ46tTDgFH~mCPkh5F9DSpm-UsyJ6SmJgm1SWfw09KbQizyp4lIwte1yumKXtORtTCKv5WFWRUFUWD6Kv1eIkh5XJ5jfMzSfaTEikZlYF4t08Lbp5Apk5-alOW-1yYIwqb5RqZhQ26w__&Key-Pair-Id=APKAJCILBXEJFZF4DCHQ", 52 | "id": 4559535, 53 | "external_id": None, 54 | "created_date": "2024-09-24 18:31:48", 55 | "updated_date": "2024-09-24 18:31:48", 56 | "img_thumbnail_url": "https://scdn.veryfi.com/other-documents/919ba4778c039560/cf1363b8-a38f-47e8-b9ee-8105342121cd/thumbnail.png?Expires=1727203608&Signature=SIRru1E-r1VT5KmufOC9A3UXlWzpgaZWUn0GhSj~veGagGAISV7sztEA7bER~kZlVnowRBSu19UaR8VeGfQ39uzUxEVlzdxPgjITt7IEgfGa~B-0EUI8izLDfRoOMkdRrOknLJKpCq87hz8fMn6wfKSgWxGgyCFKuvO2zcdla~fmtcTOrR4OMAPA3TX4Y4ZRnwCfUDQwNMw72Zihh9bxulzgjM6Cqffc7wta6wC84rYRlztPgGQj51ARcewG5s-IouvrJKoTAONLJZaq8CEc-iMh~TRzKf4MiI5HoheBFmjKb2NdoJFDpHR~~aLW8RxWkEV87JtglILAumkjrY7jjw__&Key-Pair-Id=APKAJCILBXEJFZF4DCHQ", 57 | "blueprint_name": "us_driver_license", 58 | "template_name": "us_driver_license", 59 | "address": "892 MOMONA ST HONOLULU, HI 96820", 60 | "birth_date": "1981-06-03", 61 | "expiration_date": "2008-06-03", 62 | "eyes_color": "BRO", 63 | "first_name": None, 64 | "height": "5-10", 65 | "issue_date": "1998-06-18", 66 | "last_name": "McLovin", 67 | "license_class": "3", 68 | "license_number": "01-47-87441", 69 | "sex": "M", 70 | "state": "HAWAII", 71 | "weight": "150", 72 | } 73 | 74 | client = Client(client_id="v", client_secret="w", username="o", api_key="c") 75 | responses.add( 76 | responses.POST, 77 | f"{client.versioned_url}/partner/any-documents/", 78 | json=mock, 79 | status=200, 80 | ) 81 | d = client.process_any_document_url( 82 | blueprint_name="us_driver_license", 83 | file_url="http://cdn-dev.veryfi.com/testing/veryfi-python/receipt_public.jpg", 84 | delete_after_processing=True, 85 | max_pages_to_process=1, 86 | boost_mode=True, 87 | ) 88 | assert d == mock 89 | 90 | 91 | @responses.activate 92 | def test_get_documents(): 93 | mock = [ 94 | { 95 | "pdf_url": "https://scdn.veryfi.com/other-documents/919ba4778c039560/cf1363b8-a38f-47e8-b9ee-8105342121cd/7179c430-eb38-4251-b015-9ceb20129371.pdf?Expires=1727203608&Signature=ZSfZmJRLtJ6DeIRioIQSExufnR4fDvADq1Fs-x~WnbU1JueQ1PLtY~7b~Krk7eda6EAQkMBa2wamDDcE2lCvrutHCS3jUbhlFFhSuQd1XljbYjBlWOdxYyXpYMmluDlaWlkgm41vA92UD3LSsBPBLrBasotjqNYLGnTg87guXTtUG1rSWlK2FhHxzborReNdrpXUcDMs4-kkQ46tTDgFH~mCPkh5F9DSpm-UsyJ6SmJgm1SWfw09KbQizyp4lIwte1yumKXtORtTCKv5WFWRUFUWD6Kv1eIkh5XJ5jfMzSfaTEikZlYF4t08Lbp5Apk5-alOW-1yYIwqb5RqZhQ26w__&Key-Pair-Id=APKAJCILBXEJFZF4DCHQ", 96 | "id": 4559535, 97 | "external_id": None, 98 | "created_date": "2024-09-24 18:31:48", 99 | "updated_date": "2024-09-24 18:31:48", 100 | "img_thumbnail_url": "https://scdn.veryfi.com/other-documents/919ba4778c039560/cf1363b8-a38f-47e8-b9ee-8105342121cd/thumbnail.png?Expires=1727203608&Signature=SIRru1E-r1VT5KmufOC9A3UXlWzpgaZWUn0GhSj~veGagGAISV7sztEA7bER~kZlVnowRBSu19UaR8VeGfQ39uzUxEVlzdxPgjITt7IEgfGa~B-0EUI8izLDfRoOMkdRrOknLJKpCq87hz8fMn6wfKSgWxGgyCFKuvO2zcdla~fmtcTOrR4OMAPA3TX4Y4ZRnwCfUDQwNMw72Zihh9bxulzgjM6Cqffc7wta6wC84rYRlztPgGQj51ARcewG5s-IouvrJKoTAONLJZaq8CEc-iMh~TRzKf4MiI5HoheBFmjKb2NdoJFDpHR~~aLW8RxWkEV87JtglILAumkjrY7jjw__&Key-Pair-Id=APKAJCILBXEJFZF4DCHQ", 101 | "blueprint_name": "us_driver_license", 102 | "template_name": "us_driver_license", 103 | "address": "892 MOMONA ST HONOLULU, HI 96820", 104 | "birth_date": "1981-06-03", 105 | "expiration_date": "2008-06-03", 106 | "eyes_color": "BRO", 107 | "first_name": None, 108 | "height": "5-10", 109 | "issue_date": "1998-06-18", 110 | "last_name": "McLovin", 111 | "license_class": "3", 112 | "license_number": "01-47-87441", 113 | "sex": "M", 114 | "state": "HAWAII", 115 | "weight": "150", 116 | } 117 | ] 118 | client = Client(client_id="v", client_secret="w", username="o", api_key="c") 119 | responses.add( 120 | responses.GET, 121 | f"{client.versioned_url}/partner/any-documents/", 122 | json=mock, 123 | status=200, 124 | ) 125 | d = client.get_any_documents() 126 | assert d == mock 127 | -------------------------------------------------------------------------------- /tests/test_bank_statements.py: -------------------------------------------------------------------------------- 1 | import responses 2 | 3 | from veryfi import Client 4 | 5 | 6 | MOCK = { 7 | "transactions": [ 8 | { 9 | "order": 0, 10 | "account_number": "1111111", 11 | "balance": 1803.9, 12 | "card_number": None, 13 | "credit_amount": None, 14 | "date": "2013-10-22", 15 | "debit_amount": 190.4, 16 | "description": "AUTOMATED PAY IN 650274051211-CHB\nCALL REF. NO. 3442, FROM", 17 | "transaction_id": None, 18 | "text": "22 Oct 2013 AUTOMATED PAY IN 650274051211-CHB\t\t\t\t190.40\t1803.9\nCALL REF. NO. 3442, FROM", 19 | }, 20 | { 21 | "order": 1, 22 | "account_number": "1111111", 23 | "balance": 1613.5, 24 | "card_number": None, 25 | "credit_amount": None, 26 | "date": "2013-10-22", 27 | "debit_amount": 140, 28 | "description": "DIGITAL BANKING\nA/C 22222222", 29 | "transaction_id": None, 30 | "text": "22 Oct 2013 DIGITAL BANKING\t\t\t\t\t\t140.00\t1613.5\nA/C 22222222", 31 | }, 32 | { 33 | "order": 2, 34 | "account_number": "1111111", 35 | "balance": 1473.5, 36 | "card_number": None, 37 | "credit_amount": None, 38 | "date": "2013-10-24", 39 | "debit_amount": 132.3, 40 | "description": "Amazon", 41 | "transaction_id": None, 42 | "text": "24 Oct 2013 Faster Payment\tAmazon\t\t\t\t\t132.30\t1473.5", 43 | }, 44 | { 45 | "order": 3, 46 | "account_number": "1111111", 47 | "balance": 1341.2, 48 | "card_number": None, 49 | "credit_amount": None, 50 | "date": "2013-10-24", 51 | "debit_amount": 515.22, 52 | "description": "Tebay Trading Co.", 53 | "transaction_id": None, 54 | "text": "24 Oct 2013 BACS\tTebay Trading Co.\t\t\t\t515.22\t1341.2", 55 | }, 56 | { 57 | "order": 4, 58 | "account_number": "1111111", 59 | "balance": 825.98, 60 | "card_number": None, 61 | "credit_amount": None, 62 | "date": "2013-10-25", 63 | "debit_amount": 80, 64 | "description": "Morrisons Petrol", 65 | "transaction_id": None, 66 | "text": "25 Oct 2013 Faster Payment\tMorrisons Petrol\t\t\t\t80.00\t825.98", 67 | }, 68 | { 69 | "order": 5, 70 | "account_number": "1111111", 71 | "balance": 745.98, 72 | "card_number": None, 73 | "credit_amount": 20000, 74 | "date": "2013-10-25", 75 | "debit_amount": None, 76 | "description": "Business Loan", 77 | "transaction_id": None, 78 | "text": "25 Oct 2013 BACS\tBusiness Loan\t\t20,000.00\t\t745.98", 79 | }, 80 | { 81 | "order": 6, 82 | "account_number": "1111111", 83 | "balance": 20745.98, 84 | "card_number": None, 85 | "credit_amount": None, 86 | "date": "2013-10-26", 87 | "debit_amount": 2461.55, 88 | "description": "James White Media", 89 | "transaction_id": None, 90 | "text": "26 Oct 2013 BACS\tJames White Media\t\t\t2,461.55\t20745.98", 91 | }, 92 | { 93 | "order": 7, 94 | "account_number": "1111111", 95 | "balance": 18284.43, 96 | "card_number": None, 97 | "credit_amount": None, 98 | "date": "2013-10-27", 99 | "debit_amount": 100, 100 | "description": "ATM High Street", 101 | "transaction_id": None, 102 | "text": "27 Oct 2013 Faster Payment\tATM High Street\t\t\t\t100.00\t18284.43", 103 | }, 104 | { 105 | "order": 8, 106 | "account_number": "1111111", 107 | "balance": 18184.43, 108 | "card_number": None, 109 | "credit_amount": None, 110 | "date": "2013-11-01", 111 | "debit_amount": 150, 112 | "description": "Acorn Advertising Studies", 113 | "transaction_id": None, 114 | "text": "01 Nov 2013 BACS\tAcorn Advertising Studies\t\t\t150.00\t18184.43", 115 | }, 116 | { 117 | "order": 9, 118 | "account_number": "1111111", 119 | "balance": 18034.43, 120 | "card_number": None, 121 | "credit_amount": None, 122 | "date": "2013-11-01", 123 | "debit_amount": 177, 124 | "description": "Marriott Hotel", 125 | "transaction_id": None, 126 | "text": "01 Nov 2013 BACS\tMarriott Hotel\t\t\t\t177.00\t18034.43", 127 | }, 128 | { 129 | "order": 10, 130 | "account_number": "1111111", 131 | "balance": 17857.43, 132 | "card_number": None, 133 | "credit_amount": None, 134 | "date": "2013-11-01", 135 | "debit_amount": 122.22, 136 | "description": "Abellio Scotrail Ltd", 137 | "transaction_id": None, 138 | "text": "01 Nov 2013 Faster Payment\tAbellio Scotrail Ltd\t\t\t\t122.22\t17857.43", 139 | }, 140 | { 141 | "order": 11, 142 | "account_number": "1111111", 143 | "balance": 17735.21, 144 | "card_number": None, 145 | "credit_amount": None, 146 | "date": "2013-11-01", 147 | "debit_amount": 1200, 148 | "description": "Cheque 0000234", 149 | "transaction_id": None, 150 | "text": "01 Nov 2013 CHQ\tCheque 0000234\t\t\t\t1,200.00\t17735.21", 151 | }, 152 | { 153 | "order": 12, 154 | "account_number": "1111111", 155 | "balance": 16535.21, 156 | "card_number": None, 157 | "credit_amount": 9.33, 158 | "date": "2013-12-01", 159 | "debit_amount": None, 160 | "description": "Interest Paid", 161 | "transaction_id": None, 162 | "text": "01 Dec 2013 Int. Bank\tInterest Paid\t\t\t9.33\t\t16535.21", 163 | }, 164 | { 165 | "order": 13, 166 | "account_number": "1111111", 167 | "balance": 16544.54, 168 | "card_number": None, 169 | "credit_amount": None, 170 | "date": "2013-12-01", 171 | "debit_amount": 2470, 172 | "description": "OVO Energy", 173 | "transaction_id": None, 174 | "text": "01 Dec 2013 DD\t\tOVO Energy\t\t\t\t2470.00\t16544.54", 175 | }, 176 | { 177 | "order": 14, 178 | "account_number": "1111111", 179 | "balance": 14074.54, 180 | "card_number": None, 181 | "credit_amount": None, 182 | "date": "2013-12-21", 183 | "debit_amount": 10526.4, 184 | "description": "Various Payment", 185 | "transaction_id": None, 186 | "text": "21 Dec 2013 BACS\tVarious Payment\t\t\t\t10,526.40\t14074.54", 187 | }, 188 | { 189 | "order": 15, 190 | "account_number": "1111111", 191 | "balance": 3548.14, 192 | "card_number": None, 193 | "credit_amount": None, 194 | "date": "2013-12-21", 195 | "debit_amount": 1000, 196 | "description": "HMRC", 197 | "transaction_id": None, 198 | "text": "21 Dec 2013 BACS\tHMRC\t\t\t\t\t1,000.00\t3548.14", 199 | }, 200 | { 201 | "order": 16, 202 | "account_number": "1111111", 203 | "balance": 2548.14, 204 | "card_number": None, 205 | "credit_amount": None, 206 | "date": "2013-12-21", 207 | "debit_amount": 280, 208 | "description": "DVLA", 209 | "transaction_id": None, 210 | "text": "21 Dec 2013 DD\t\tDVLA\t\t\t\t\t280.00\t2548.14", 211 | }, 212 | ], 213 | "summaries": [{"name": "Paid Out", "total": 2684.1}, {"name": "Paid In", "total": 2180.4}], 214 | "account_numbers": ["1111111"], 215 | "routing_numbers": ["16-10-00"], 216 | "pdf_url": "https://scdn.veryfi.com/bank_statements/919ba4778c039560/f02a38ed-e486-4d30-8354-23c25a0a4446/fe286c1b-bbdb-4a10-b9a8-81546f229de8.pdf?Expires=1727204326&Signature=K8v0yzgwW4NjvFC6k9smjmyznCirxI3z12ODX233hwtdnh9dY3DoqauItMoIkzcG6XL6y5sFoQPlisbck4FSyAXtEUGGGKgCgiezte3y4xMt43~zPR4WWbojPp4zGvZQpBwkxscGI-EFEcBPzLth2GGE0geYNg6R~nKeLn0mQ0rknTiqt4ras70-xC0KsgfwLdYa2xY8Kq56XtjgrsoKJSGIwFaUn0NmML8x~lTr3ifhp5s1t5KnylKDkaNynUNI2hCEUouqILknmmYmi8yLcL9BY3U9vZj0SbobqOnNbN41cMzPfGVm6WTBY~PCrAFNhal03L583hUhy5KHQjBzmQ__&Key-Pair-Id=APKAJCILBXEJFZF4DCHQ", 217 | "id": 4559568, 218 | "external_id": None, 219 | "created_date": "2024-09-24 18:43:46", 220 | "updated_date": "2024-09-24 18:43:46", 221 | "img_thumbnail_url": "https://scdn.veryfi.com/bank_statements/919ba4778c039560/f02a38ed-e486-4d30-8354-23c25a0a4446/thumbnail.png?Expires=1727204326&Signature=V9YEjDC-W-XaMLsWOFdKmC2X0h9moWf5Mh-j-5UEGNdeyf4dvYCCK6ByXubxeu-fzwTfCpgKl25y7CyCynsH~URsX~1wxsWE~bSLPA7CwkL54NpLmsmrksgAIdU67iV-O-ZDEwdOIQHBX5bUd2QaiGsK3wF2Z~xX5ouOMX3UkzPujYCBZtMB4pbidJmbuB6vC51y8V-yDbuRdHyoYA7ixNiczrSSuqpmkix6KC7ZGIu4eiIBPUnCpIlFc9~qaWkKeecJlLBeOM6SknJD~xTlYxGanbCWbvPYvIFeZX2sWUJPG2A9t~nknMsENymtGqrgy78kcweuv4uyUAbWMr-GxA__&Key-Pair-Id=APKAJCILBXEJFZF4DCHQ", 222 | "account_holder_address": "San Mateo", 223 | "account_holder_name": "Mr Robot Roboto", 224 | "account_number": "1111111", 225 | "account_type": "CURRENT ACCOUNT", 226 | "bank_address": "The Mound, Edinburgh EH1 1YZ.", 227 | "bank_name": "Royal Bank of Scotland Plc.", 228 | "bank_website": None, 229 | "beginning_balance": 1803.9, 230 | "due_date": None, 231 | "ending_balance": 300.2, 232 | "minimum_due": None, 233 | "period_end_date": "2023-12-21", 234 | "period_start_date": "2023-10-22", 235 | "routing_number": "16-10-00", 236 | "statement_date": None, 237 | "statement_number": None, 238 | "currency_code": "GBP", 239 | "iban_number": "GB11RBOS 1610 0011 1111 11", 240 | "swift": "RBOSGB2L", 241 | "account_vat_number": None, 242 | "bank_vat_number": "SC327000", 243 | } 244 | 245 | 246 | @responses.activate 247 | def test_process_bank_statement_url(): 248 | 249 | client = Client(client_id="v", client_secret="w", username="o", api_key="c") 250 | responses.add( 251 | responses.POST, 252 | f"{client.versioned_url}/partner/bank-statements/", 253 | json=MOCK, 254 | status=200, 255 | ) 256 | d = client.process_bank_statement_document_url( 257 | file_url="http://cdn-dev.veryfi.com/testing/veryfi-python/receipt_public.jpg" 258 | ) 259 | assert d == MOCK 260 | 261 | 262 | @responses.activate 263 | def test_process_bank_statement(): 264 | 265 | client = Client(client_id="v", client_secret="w", username="o", api_key="c") 266 | responses.add( 267 | responses.POST, 268 | f"{client.versioned_url}/partner/bank-statements/", 269 | json=MOCK, 270 | status=200, 271 | ) 272 | d = client.process_bank_statement_document(file_path="tests/assets/receipt_public.jpg") 273 | assert d == MOCK 274 | 275 | 276 | @responses.activate 277 | def test_get_bank_statements(): 278 | mock = [MOCK] 279 | 280 | client = Client(client_id="v", client_secret="w", username="o", api_key="c") 281 | responses.add( 282 | responses.GET, 283 | f"{client.versioned_url}/partner/bank-statements/", 284 | json=mock, 285 | status=200, 286 | ) 287 | d = client.get_bank_statements() 288 | assert d == mock 289 | -------------------------------------------------------------------------------- /tests/test_bussines_cards.py: -------------------------------------------------------------------------------- 1 | import responses 2 | 3 | from veryfi import Client 4 | 5 | 6 | MOCK = { 7 | "text": "Dmitry Birulia\ndmitry@veryfi.com\nVERYFI\twww.veryfi.com", 8 | "id": 4662609, 9 | "external_id": None, 10 | "created_date": "2024-10-29 19:41:34", 11 | "updated_date": "2024-10-29 19:41:34", 12 | "organization": "VERYFI", 13 | "logo_url": "https://cdn.veryfi.com/logos/us/421973497.png", 14 | "img_url": "https://scdn.veryfi.com/business_cards/919ba4778c039560/235aaf5b-e464-4e03-adc2-443c39f1c053/d42e1e30-4170-4c81-927d-17c31317a94a.jpg?Expires=1730231794&Signature=bP807q99xtc0BcMPVD0SrnFw~aqr-zFUcJhlSEEKX2LnM2QnX4yDFdjITazqKbyJUjTxz1DtEuEN~fkN1ArE5C7u0Q70lLDsMcShRQgWylaQ45S3ulaECcwusdPnmJWczeGzyFj9CQ2RUvUfemuSffg8igP80~~MKdts0I9OJO4eg5FMV4Sh5cOg1y8H2DTbgPZHKO6VHMXQzBh8EW5PlM4IuCZAg5xKrJK2-XF~B8xCDzBECRMAVBjEu8hlm8SFs7D07LR9d6bXZMKv8egZ1HzyhTVdzm-TVBPNM328ou1qIGwLAOxq9PGJK3~2Vd9TSq5ONgPUesh8RAyc-NORcA__&Key-Pair-Id=APKAJCILBXEJFZF4DCHQ", 15 | "person": "Dmitry Birulia", 16 | "parsed_name": {"family_name": "Birulia", "given_name": "Dmitry"}, 17 | "title": None, 18 | "email": "dmitry@veryfi.com", 19 | "address": None, 20 | "parsed_address": {}, 21 | "mobile": None, 22 | "phone": None, 23 | "fax": None, 24 | "web": "www.veryfi.com", 25 | } 26 | 27 | 28 | @responses.activate 29 | def test_process_business_card_url(): 30 | 31 | client = Client(client_id="v", client_secret="w", username="o", api_key="c") 32 | responses.add( 33 | responses.POST, 34 | f"{client.versioned_url}/partner/business-cards/", 35 | json=MOCK, 36 | status=200, 37 | ) 38 | d = client.process_bussines_card_document_url( 39 | file_url="http://cdn-dev.veryfi.com/testing/veryfi-python/receipt_public.jpg" 40 | ) 41 | assert d == MOCK 42 | 43 | 44 | @responses.activate 45 | def test_process_business_card(): 46 | 47 | client = Client(client_id="v", client_secret="w", username="o", api_key="c") 48 | responses.add( 49 | responses.POST, 50 | f"{client.versioned_url}/partner/business-cards/", 51 | json=MOCK, 52 | status=200, 53 | ) 54 | d = client.process_bussines_card_document(file_path="tests/assets/receipt_public.jpg") 55 | assert d == MOCK 56 | 57 | 58 | @responses.activate 59 | def test_get_business_cards(): 60 | mock = [MOCK] 61 | 62 | client = Client(client_id="v", client_secret="w", username="o", api_key="c") 63 | responses.add( 64 | responses.GET, 65 | f"{client.versioned_url}/partner/business-cards/", 66 | json=mock, 67 | status=200, 68 | ) 69 | d = client.get_business_cards() 70 | assert d == mock 71 | -------------------------------------------------------------------------------- /tests/test_checks.py: -------------------------------------------------------------------------------- 1 | import responses 2 | 3 | from veryfi import Client 4 | 5 | 6 | MOCK = { 7 | "pdf_url": "https://scdn.veryfi.com/checks/919ba4778c039560/78535c60-e371-40fd-ae78-6d0c019b2c35/067e7057-e38b-4c32-9d87-720e0a8e232f.pdf?Expires=1730233783&Signature=fbR-arwLoH1YC8GVK52SvvilH59eHWIYp1o2WXw5UWr0s0CjKkrJ1Bx-PORKVzLbnJHOoYJDC4lU1iiqdq4~yDyz~-ygRHTNxyT9BJovFSzCBAc3Gnzv7uWMNIp-9mdV0QGk-Fu25eZfwd56Dfd2ZhG-EzreCRfh66r6338UF4EaHK5SG5b4i-NwkDaZ~qRZC6jNzYUJOGbXexYPbQxF5tMinc97ok~~fLQ--r0HWr7SvQyJisUqDnKS0DMTQujDz-7lStMJmvvlQX0jmpdcsq8DBIR6SnWZxHA7tM-ydD27Jt8l753X3uNtZuao61CeGSpQP09CnWlctTewm4IHKQ__&Key-Pair-Id=APKAJCILBXEJFZF4DCHQ", 8 | "id": 4662680, 9 | "external_id": None, 10 | "created_date": "2024-10-29 20:14:43", 11 | "updated_date": "2024-10-29 20:14:43", 12 | "img_thumbnail_url": "https://scdn.veryfi.com/checks/919ba4778c039560/78535c60-e371-40fd-ae78-6d0c019b2c35/thumbnail.jpg?Expires=1730233783&Signature=Sy8RUK0FLkeyhI3tpR8d60j-4BWyUH82D1frNU5FGGgGqumWWer6JgXbqM2eoHrgz04kwtKMwC-UZME0AZa-HtZr8j6a7TRO6M2uT0GoCHNQKv7rNcWUPajn4GsdU8VyY3b8KDx7WaGLd3VXP1TWAIqhW~DC07ZzjWbJ3K~8Ieyztt6Naijb~JbDFgUuIcNu8oikfoK3GAE8vzyElU5ctX4nmG1H-BEySCY-eIjf7GVwvaKjnZjab4h0Ox8SIdRyqVp~sj7dwlAtMlJmv6TxsNS14EDi91pxWPcvQlD7JCyQOH6brsgD0pkYVa4JFBF7yUTnGS4NXcCTiqkBhLh1Tw__&Key-Pair-Id=APKAJCILBXEJFZF4DCHQ", 13 | "text": "THIS DOCUMENT WAS PRINTED ON PAPER CONTAINING ULTRAVIOLET FIBERS AND AN ARTIFICIAL WATERMARK ON BACK\n\n\t70-2328/719 IL\nNationwide\nis on your side\t\t\tOctober 4, 2021\t\tCheck No. 118408359\nPO Box 2344\nBrea, CA 92822-2344\nPay: One Thousand Three Hundred Eight And 45/100 Dollars\t\t\t$1,308.45\n\nPay to the\tDmitry Birulia\nOrder of:\t733 Long Bridge\nSan Francisco CA 94158\n\n\tAUTHORIZED SIGNATURE\nMemo :F-602441-2021092406733\t\t\t\t\tVOID AFTER SIX MONTHS\n\n⑈0118408359⑈ ⑆031923284⑆ 8765129397⑈", 14 | "meta": {}, 15 | "amount": 1308.45, 16 | "amount_text": "One Thousand Three Hundred Eight And 45/100 Dollars", 17 | "bank_address": "Brea, CA 92822-2344", 18 | "bank_name": None, 19 | "fractional_routing_number": "70-2328/719", 20 | "routing_from_fractional": "071923284", 21 | "check_number": "0118408359", 22 | "date": "2021-10-04", 23 | "memo": "F-602441-2021092406733", 24 | "payer_address": "PO Box 2344", 25 | "payer_name": None, 26 | "receiver_address": "733 Long Bridge\nSan Francisco CA 94158", 27 | "receiver_name": "Dmitry Birulia", 28 | "is_signed": True, 29 | "is_endorsed": None, 30 | "endorsement": {"is_signed": None, "is_mobile_or_remote_deposit_only": None}, 31 | "micr": { 32 | "routing_number": "031923284", 33 | "account_number": "8765129397", 34 | "serial_number": None, 35 | "raw": "C0118408359C A031923284A 8765129397C", 36 | }, 37 | } 38 | 39 | 40 | @responses.activate 41 | def test_process_check_url(): 42 | 43 | client = Client(client_id="v", client_secret="w", username="o", api_key="c") 44 | responses.add( 45 | responses.POST, 46 | f"{client.versioned_url}/partner/checks/", 47 | json=MOCK, 48 | status=200, 49 | ) 50 | d = client.process_check_url( 51 | file_url="http://cdn-dev.veryfi.com/testing/veryfi-python/receipt_public.jpg" 52 | ) 53 | assert d == MOCK 54 | 55 | 56 | @responses.activate 57 | def test_process_check(): 58 | 59 | client = Client(client_id="v", client_secret="w", username="o", api_key="c") 60 | responses.add( 61 | responses.POST, 62 | f"{client.versioned_url}/partner/checks/", 63 | json=MOCK, 64 | status=200, 65 | ) 66 | d = client.process_check(file_path="tests/assets/receipt_public.jpg") 67 | assert d == MOCK 68 | 69 | 70 | @responses.activate 71 | def test_get_checks(): 72 | mock = [MOCK] 73 | 74 | client = Client(client_id="v", client_secret="w", username="o", api_key="c") 75 | responses.add( 76 | responses.GET, 77 | f"{client.versioned_url}/partner/checks/", 78 | json=mock, 79 | status=200, 80 | ) 81 | d = client.get_checks() 82 | assert d == mock 83 | -------------------------------------------------------------------------------- /tests/test_documents.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | import responses 3 | 4 | from veryfi import Client 5 | 6 | 7 | @pytest.mark.parametrize("client_secret", [None, "s"]) 8 | @responses.activate 9 | def test_process_document(client_secret): 10 | mock = { 11 | "abn_number": "", 12 | "account_number": "", 13 | "barcodes": [], 14 | "bill_to_address": "", 15 | "bill_to_name": "", 16 | "bill_to_vat_number": "", 17 | "card_number": "", 18 | "cashback": 0.0, 19 | "category": "", 20 | "created": "2021-06-22 20:11:10", 21 | "currency_code": "USD", 22 | "date": "2021-06-22 16:11:10", 23 | "discount": 0.0, 24 | "due_date": "", 25 | "external_id": "", 26 | "id": 36966934, 27 | "img_file_name": "7a0371f1-f695-4f9b-9e2b-da54cdf189fc.jpg", 28 | "img_thumbnail_url": "", 29 | "img_url": "", 30 | "invoice_number": "98", 31 | "line_items": [ 32 | { 33 | "date": "", 34 | "description": "98 Meat Pty Xchz", 35 | "discount": 0.0, 36 | "id": 67185481, 37 | "order": 0, 38 | "price": 0.0, 39 | "quantity": 1.0, 40 | "reference": "", 41 | "sku": "", 42 | "tax": 0.0, 43 | "tax_rate": 0.0, 44 | "total": 90.85, 45 | "type": "food", 46 | "unit_of_measure": "", 47 | } 48 | ], 49 | "notes": "", 50 | "ocr_text": "\n\x0c2004-10-31\n\t8:21 PM\nYOUR GUEST NUMBER IS\n98\nIN-N-OUT BURGER LAS VEGAS EASTERN\n2004-10-31\t\t8:21 PM\n165 1 5 98\nCashier: SAM\nGUEST #: 98\nCounter-Eat in\n\t2.65\nDbDb\t\t88.20\n98 Meat Pty Xchz\n\t90.85\nCounter-Eat In\t\t6.81\nTAX 7.50%\t\t97.66\nAmount Due\n\t$97.66\nCASH TENDER\t\t$.00\nChange\n2004-10-31\t\t8:21 PM\nTHANK YOU!\n", 51 | "payment_display_name": "Cash", 52 | "payment_terms": "", 53 | "payment_type": "cash", 54 | "phone_number": "", 55 | "purchase_order_number": "", 56 | "reference_number": "VBIJG-6934", 57 | "rounding": 0.0, 58 | "service_end_date": "", 59 | "service_start_date": "", 60 | "shipping": 0.0, 61 | "subtotal": 0.0, 62 | "tags": [], 63 | "tax": 97.66, 64 | "tax_lines": [{"base": 0.0, "name": "", "order": 0, "rate": 7.5, "total": 97.66}], 65 | "tip": 0.0, 66 | "total": 97.66, 67 | "tracking_number": "", 68 | "updated": "2021-06-22 20:11:11", 69 | "vat_number": "", 70 | "vendor": { 71 | "address": "", 72 | "email": "", 73 | "fax_number": "", 74 | "name": "In-N-Out Burger", 75 | "phone_number": "", 76 | "raw_name": "In-N-Out Burger", 77 | "vendor_logo": "https://cdn.veryfi.com/logos/us/949103001.png", 78 | "vendor_reg_number": "", 79 | "vendor_type": "Restaurant", 80 | "web": "", 81 | }, 82 | "vendor_account_number": "", 83 | "vendor_bank_name": "", 84 | "vendor_bank_number": "", 85 | "vendor_bank_swift": "", 86 | "vendor_iban": "", 87 | } 88 | client = Client(client_id="v", client_secret=client_secret, username="o", api_key="c") 89 | responses.add( 90 | responses.POST, 91 | f"{client.versioned_url}/partner/documents/", 92 | json=mock, 93 | status=200, 94 | ) 95 | d = client.process_document( 96 | file_path="tests/assets/receipt_public.jpg", delete_after_processing=True, boost_mode=True 97 | ) 98 | assert d == mock 99 | 100 | 101 | @responses.activate 102 | def test_process_document_url(): 103 | mock = { 104 | "abn_number": "", 105 | "account_number": "", 106 | "barcodes": [], 107 | "bill_to_address": "", 108 | "bill_to_name": "", 109 | "bill_to_vat_number": "", 110 | "card_number": "", 111 | "cashback": 0.0, 112 | "category": "", 113 | "created": "2021-06-22 20:31:04", 114 | "currency_code": "USD", 115 | "date": "2021-06-22 16:31:04", 116 | "discount": 0.0, 117 | "due_date": "", 118 | "external_id": "", 119 | "id": 36967602, 120 | "img_file_name": "962748f4-95bb-400b-9f59-9c2d5e42e6af.jpg", 121 | "img_thumbnail_url": "", 122 | "img_url": "", 123 | "invoice_number": "98", 124 | "line_items": [ 125 | { 126 | "date": "", 127 | "description": "98 Meat Pty Xchz", 128 | "discount": 0.0, 129 | "id": 67188771, 130 | "order": 0, 131 | "price": 0.0, 132 | "quantity": 1.0, 133 | "reference": "", 134 | "sku": "", 135 | "tax": 0.0, 136 | "tax_rate": 0.0, 137 | "total": 90.85, 138 | "type": "food", 139 | "unit_of_measure": "", 140 | } 141 | ], 142 | "notes": "", 143 | "ocr_text": "\n\x0c2004-10-31\n\t8:21 PM\nYOUR GUEST NUMBER IS\n98\nIN-N-OUT BURGER LAS VEGAS EASTERN\n2004-10-31\t\t8:21 PM\n165 1 5 98\nCashier: SAM\nGUEST #: 98\nCounter-Eat in\n\t2.65\nDbDb\t\t88.20\n98 Meat Pty Xchz\n\t90.85\nCounter-Eat In\t\t6.81\nTAX 7.50%\t\t97.66\nAmount Due\n\t$97.66\nCASH TENDER\t\t$.00\nChange\n2004-10-31\t\t8:21 PM\nTHANK YOU!\n", 144 | "payment_display_name": "Cash", 145 | "payment_terms": "", 146 | "payment_type": "cash", 147 | "phone_number": "", 148 | "purchase_order_number": "", 149 | "reference_number": "VBIJG-7602", 150 | "rounding": 0.0, 151 | "service_end_date": "", 152 | "service_start_date": "", 153 | "shipping": 0.0, 154 | "subtotal": 0.0, 155 | "tags": [], 156 | "tax": 97.66, 157 | "tax_lines": [{"base": 0.0, "name": "", "order": 0, "rate": 7.5, "total": 97.66}], 158 | "tip": 0.0, 159 | "total": 97.66, 160 | "tracking_number": "", 161 | "updated": "2021-06-22 20:31:05", 162 | "vat_number": "", 163 | "vendor": { 164 | "address": "", 165 | "email": "", 166 | "fax_number": "", 167 | "name": "In-N-Out Burger", 168 | "phone_number": "", 169 | "raw_name": "In-N-Out Burger", 170 | "vendor_logo": "https://cdn.veryfi.com/logos/us/949103001.png", 171 | "vendor_reg_number": "", 172 | "vendor_type": "Restaurant", 173 | "web": "", 174 | }, 175 | "vendor_account_number": "", 176 | "vendor_bank_name": "", 177 | "vendor_bank_number": "", 178 | "vendor_bank_swift": "", 179 | "vendor_iban": "", 180 | } 181 | 182 | client = Client(client_id="v", client_secret="w", username="o", api_key="c") 183 | responses.add( 184 | responses.POST, 185 | f"{client.versioned_url}/partner/documents/", 186 | json=mock, 187 | status=200, 188 | ) 189 | d = client.process_document_url( 190 | file_url="http://cdn-dev.veryfi.com/testing/veryfi-python/receipt_public.jpg", 191 | categories=["Fat"], 192 | delete_after_processing=True, 193 | max_pages_to_process=1, 194 | boost_mode=True, 195 | ) 196 | assert d == mock 197 | 198 | 199 | @responses.activate 200 | def test_get_documents(): 201 | mock = [ 202 | { 203 | "abn_number": "", 204 | "account_number": "", 205 | "barcodes": [], 206 | "bill_to_address": "", 207 | "bill_to_name": "", 208 | "bill_to_vat_number": "", 209 | "card_number": "", 210 | "cashback": 0.0, 211 | "category": "", 212 | "created": "2021-06-22 20:31:04", 213 | "currency_code": "USD", 214 | "date": "2021-06-22 16:31:04", 215 | "discount": 0.0, 216 | "due_date": "", 217 | "external_id": "", 218 | "id": 36967602, 219 | "img_file_name": "962748f4-95bb-400b-9f59-9c2d5e42e6af.jpg", 220 | "img_thumbnail_url": "", 221 | "img_url": "", 222 | "invoice_number": "98", 223 | "line_items": [ 224 | { 225 | "date": "", 226 | "description": "98 Meat Pty Xchz", 227 | "discount": 0.0, 228 | "id": 67188771, 229 | "order": 0, 230 | "price": 0.0, 231 | "quantity": 1.0, 232 | "reference": "", 233 | "sku": "", 234 | "tax": 0.0, 235 | "tax_rate": 0.0, 236 | "total": 90.85, 237 | "type": "food", 238 | "unit_of_measure": "", 239 | } 240 | ], 241 | "notes": "", 242 | "ocr_text": "\n\x0c2004-10-31\n\t8:21 PM\nYOUR GUEST NUMBER IS\n98\nIN-N-OUT BURGER LAS VEGAS EASTERN\n2004-10-31\t\t8:21 PM\n165 1 5 98\nCashier: SAM\nGUEST #: 98\nCounter-Eat in\n\t2.65\nDbDb\t\t88.20\n98 Meat Pty Xchz\n\t90.85\nCounter-Eat In\t\t6.81\nTAX 7.50%\t\t97.66\nAmount Due\n\t$97.66\nCASH TENDER\t\t$.00\nChange\n2004-10-31\t\t8:21 PM\nTHANK YOU!\n", 243 | "payment_display_name": "Cash", 244 | "payment_terms": "", 245 | "payment_type": "cash", 246 | "phone_number": "", 247 | "purchase_order_number": "", 248 | "reference_number": "VBIJG-7602", 249 | "rounding": 0.0, 250 | "service_end_date": "", 251 | "service_start_date": "", 252 | "shipping": 0.0, 253 | "subtotal": 0.0, 254 | "tags": [], 255 | "tax": 97.66, 256 | "tax_lines": [{"base": 0.0, "name": "", "order": 0, "rate": 7.5, "total": 97.66}], 257 | "tip": 0.0, 258 | "total": 97.66, 259 | "tracking_number": "", 260 | "updated": "2021-06-22 20:31:05", 261 | "vat_number": "", 262 | "vendor": { 263 | "address": "", 264 | "email": "", 265 | "fax_number": "", 266 | "name": "In-N-Out Burger", 267 | "phone_number": "", 268 | "raw_name": "In-N-Out Burger", 269 | "vendor_logo": "https://cdn.veryfi.com/logos/us/949103001.png", 270 | "vendor_reg_number": "", 271 | "vendor_type": "Restaurant", 272 | "web": "", 273 | }, 274 | "vendor_account_number": "", 275 | "vendor_bank_name": "", 276 | "vendor_bank_number": "", 277 | "vendor_bank_swift": "", 278 | "vendor_iban": "", 279 | } 280 | ] 281 | client = Client(client_id="v", client_secret="w", username="o", api_key="c") 282 | responses.add( 283 | responses.GET, 284 | f"{client.versioned_url}/partner/documents/", 285 | json=mock, 286 | status=200, 287 | ) 288 | d = client.get_documents( 289 | q="In-N-Out Burger", 290 | created_gte="2021-05-22+00:00:00", 291 | **{"created_lt": "2021-07-22+00:00:00"}, 292 | ) 293 | assert d == mock 294 | -------------------------------------------------------------------------------- /tests/test_errors.py: -------------------------------------------------------------------------------- 1 | import responses 2 | import requests 3 | 4 | from veryfi.errors import BadRequest, VeryfiClientError 5 | from veryfi import Client 6 | import pytest 7 | 8 | 9 | @responses.activate 10 | def test_bad_request(): 11 | url = f"{Client.BASE_URL}v8/partner/documents" 12 | responses.add( 13 | responses.POST, 14 | url, 15 | json={"status": "fail", "error": "Bad or missing parameters"}, 16 | status=400, 17 | ) 18 | response = requests.post(url) 19 | 20 | with pytest.raises(BadRequest, match="400, Bad or missing parameters") as e: 21 | raise VeryfiClientError.from_response(response) 22 | 23 | 24 | @responses.activate 25 | def test_not_found(): 26 | url = f"{Client.BASE_URL}v7/partner/documents" 27 | responses.add( 28 | responses.PUT, 29 | url, 30 | json={"status": "fail", "error": "Document not found"}, 31 | status=404, 32 | ) 33 | response = requests.put(url) 34 | 35 | with pytest.raises(VeryfiClientError, match="404, Document not found") as e: 36 | raise VeryfiClientError.from_response(response) 37 | -------------------------------------------------------------------------------- /tests/test_w2s.py: -------------------------------------------------------------------------------- 1 | import responses 2 | 3 | from veryfi import Client 4 | 5 | 6 | MOCK = { 7 | "pdf_url": None, 8 | "id": 4559395, 9 | "external_id": None, 10 | "created_date": "2024-09-24 18:04:25", 11 | "updated_date": "2024-09-24 18:04:25", 12 | "img_thumbnail_url": None, 13 | "advance_eic_payment": None, 14 | "employee_ssn": "123-45-6789", 15 | "ein": "11-2233445", 16 | "employer_name": "The Big Company", 17 | "employer_address": "123 Main Street\nAnywhere, PA 12345", 18 | "control_number": "A1B2", 19 | "employee_name": "Jane A DOE", 20 | "employee_address": "123 Elm Street\nAnywhere Else, PA 23456", 21 | "wages_other_comps": 48500, 22 | "federal_income_tax": 6835, 23 | "ss_wages": 50000, 24 | "ss_tax": 3100, 25 | "medicare_wages": 50000, 26 | "medicare_tax": 725, 27 | "ss_tips": None, 28 | "allocated_tips": None, 29 | "dependent_care_benefits": None, 30 | "non_qualified_plans": None, 31 | "state": "PAL", 32 | "employer_state_id": "1235", 33 | "state_wages_tips": 50000, 34 | "state_income_tax": 1535, 35 | "local_wages_tips": 50000, 36 | "local_income_tax": 750, 37 | "locality_name": "MU", 38 | "field_12a_col1": "D", 39 | "field_12a_col2": 1500, 40 | "field_12b_col1": "DD", 41 | "field_12b_col2": 1000, 42 | "field_12c_col1": "P", 43 | "field_12c_col2": 4800, 44 | "field_12d_col1": None, 45 | "field_12d_col2": None, 46 | "is_13a": False, 47 | "is_13b": True, 48 | "is_13c": False, 49 | "states": [ 50 | { 51 | "state": "PAL", 52 | "employer_state_id": "1235", 53 | "state_wages_tips": 50000, 54 | "state_income_tax": 1535, 55 | "local_wages_tips": 50000, 56 | "local_income_tax": 750, 57 | "locality_name": "MU", 58 | } 59 | ], 60 | "field_14_other": [], 61 | } 62 | 63 | 64 | @responses.activate 65 | def test_process_w2_url(): 66 | 67 | client = Client(client_id="v", client_secret="w", username="o", api_key="c") 68 | responses.add( 69 | responses.POST, 70 | f"{client.versioned_url}/partner/w2s/", 71 | json=MOCK, 72 | status=200, 73 | ) 74 | d = client.process_w2_document_url( 75 | file_url="http://cdn-dev.veryfi.com/testing/veryfi-python/receipt_public.jpg" 76 | ) 77 | assert d == MOCK 78 | 79 | 80 | @responses.activate 81 | def test_process_w2(): 82 | 83 | client = Client(client_id="v", client_secret="w", username="o", api_key="c") 84 | responses.add( 85 | responses.POST, 86 | f"{client.versioned_url}/partner/w2s/", 87 | json=MOCK, 88 | status=200, 89 | ) 90 | d = client.process_w2_document(file_path="tests/assets/receipt_public.jpg") 91 | assert d == MOCK 92 | 93 | 94 | @responses.activate 95 | def test_get_w2s(): 96 | mock = [MOCK] 97 | 98 | client = Client(client_id="v", client_secret="w", username="o", api_key="c") 99 | responses.add( 100 | responses.GET, 101 | f"{client.versioned_url}/partner/w2s/", 102 | json=mock, 103 | status=200, 104 | ) 105 | d = client.get_w2s() 106 | assert d == mock 107 | -------------------------------------------------------------------------------- /tests/test_w8s.py: -------------------------------------------------------------------------------- 1 | import responses 2 | 3 | from veryfi import Client 4 | 5 | 6 | MOCK = { 7 | "pdf_url": "https://scdn.veryfi.com/w8s/919ba4778c039560/64ea51e9-4293-4a0e-99a6-8c3cd0a4f7ab/8468d271-2943-46e7-b9a2-3dd0372d3648.pdf?Expires=1730234368&Signature=e1bOil~yK4P4uKYLxX1NPfHA3PxiTxd-Ds4HVnjNXxY22D-ng2NGJQQrWAst5E0ionsdkkFPc7mKy0fp6MkmwnZF~I-j8e1P9fhbI-T-0NhiDji4gp6xt4~vm-i9MG34K~Xa3TWPA~kMbQ~Hj2gjiMMniXsH6HeqH99yfl-Vt2ZWEMdWl3~ZlMWEpnPVIzDXdDBc~uRYCOS0KiLD2pfNAORYwp1ayNiuhiJzucJPAfRuK00y0BoUEPBmBS-aLa62VhNYVKmUPtVNobS2MjcGcnqnBhOZlbw0B5VTLNqrSIgKSVy6I6Co4zAwLjgviQyoPArVtgmJR8UNdFRk9LuDqw__&Key-Pair-Id=APKAJCILBXEJFZF4DCHQ", 8 | "id": 4662698, 9 | "external_id": None, 10 | "created_date": "2024-10-29 20:24:28", 11 | "updated_date": "2024-10-29 20:24:28", 12 | "img_thumbnail_url": "https://scdn.veryfi.com/w8s/919ba4778c039560/64ea51e9-4293-4a0e-99a6-8c3cd0a4f7ab/thumbnail.jpg?Expires=1730234368&Signature=ch2MJSewRZV3LkSlMVKMK1~BXsUiO~wNT5bSllXXv1N85jGIqsJHYTkrTaL1fXSZERLXBC6DFIzOcgSYB~zPu3r3nzr7v2Q-WBc~jk8tSxRWGg4eYLkwW34h-RwhckXuiH6UCo6Q01SF6P4RAt9~YL4mIXOLmeahsjFQ-w0VHVuqsBQrsVJYoft7N-VXGgo-SRxKHBKX1eWEqYkV3hZJHslUxQvb0V1m3hEBuKHlj4gX5LcEHv-8wj90QlFujFUkRjJpMLqgsi3z-McQSOHNBc6fi3WqOWspiPzzUTQMqMswRPnkOMTlOIqNBoDO6oOrOIHR4CNtN6DZx9KjGTFSCQ__&Key-Pair-Id=APKAJCILBXEJFZF4DCHQ", 13 | "certify_checkbox": False, 14 | "field_1_name": None, 15 | "field_2_country": None, 16 | "field_3_disregarded_entity_name": None, 17 | "field_4_checkbox_central_issue_bank": False, 18 | "field_4_checkbox_complex_trust": False, 19 | "field_4_checkbox_corporation": False, 20 | "field_4_checkbox_disregarded_entity": False, 21 | "field_4_checkbox_estate": False, 22 | "field_4_checkbox_foreign_government_controlled_entity": False, 23 | "field_4_checkbox_foreign_government_integral_part": False, 24 | "field_4_checkbox_grantor_trust": False, 25 | "field_4_checkbox_hybrid_no": False, 26 | "field_4_checkbox_hybrid_yes": False, 27 | "field_4_checkbox_international_organization": False, 28 | "field_4_checkbox_partnership": False, 29 | "field_4_checkbox_private_foundation": False, 30 | "field_4_checkbox_simple_trust": False, 31 | "field_4_checkbox_tax_exempt_organization": False, 32 | "field_5_checkbox_active_nffe": False, 33 | "field_5_checkbox_certain_investment_entities_that_do_not_maintain_financial_accounts": False, 34 | "field_5_checkbox_certified_deemed_compliant_ffi_low_value_accounts": False, 35 | "field_5_checkbox_certified_deemed_compliant_limited_life_debt_investment_entity": False, 36 | "field_5_checkbox_certified_deemed_compliant_nonregistering_local_bank": False, 37 | "field_5_checkbox_certified_deemed_compliant_sponsored_closely_held_investment_vehicle": False, 38 | "field_5_checkbox_direct_reporting_nffe": False, 39 | "field_5_checkbox_entity_wholly_owned_exempt": False, 40 | "field_5_checkbox_excepted_inter_affiliate_ffi": False, 41 | "field_5_checkbox_excepted_nonfiancial_entity_bankruptcy": False, 42 | "field_5_checkbox_excepted_nonfiancial_group_entity": False, 43 | "field_5_checkbox_excepted_nonfiancial_start_up": False, 44 | "field_5_checkbox_excepted_territory_nffe": False, 45 | "field_5_checkbox_exempt_retirement_plans": False, 46 | "field_5_checkbox_foreign_government": False, 47 | "field_5_checkbox_international_organization": False, 48 | "field_5_checkbox_nonparticipating_ffi": False, 49 | "field_5_checkbox_nonprofit": False, 50 | "field_5_checkbox_nonreporting_iga_ffi": False, 51 | "field_5_checkbox_not_financial_account": False, 52 | "field_5_checkbox_organization_501c": False, 53 | "field_5_checkbox_owner_documented_ffi": True, 54 | "field_5_checkbox_participating_ffi": False, 55 | "field_5_checkbox_passive_nffe": False, 56 | "field_5_checkbox_publicly_traded_nffe": False, 57 | "field_5_checkbox_registered_deemed_compliant_ffi": False, 58 | "field_5_checkbox_reporting_model_one_ffi": False, 59 | "field_5_checkbox_reporting_model_two_ffi": False, 60 | "field_5_checkbox_restricted_distributor": False, 61 | "field_5_checkbox_sponsored_direct_reporting_nffe": False, 62 | "field_5_checkbox_sponsored_ffi": False, 63 | "field_5_checkbox_territory_financial_institutions": False, 64 | "field_6_address": None, 65 | "field_6_city": None, 66 | "field_6_country": None, 67 | "field_7_mailing_city": None, 68 | "field_7_mailing_country": None, 69 | "field_7_mailing_street": None, 70 | "field_8_tin": None, 71 | "field_9a_giin": None, 72 | "field_9b_foreign_tin": None, 73 | "field_9c_checkbox_tin_not_required": False, 74 | "field_10_reference_number": None, 75 | "field_11_checkbox_branch_nonparticipating_ffi": False, 76 | "field_11_checkbox_participating_ffi": False, 77 | "field_11_checkbox_reporting_model_one_ffi": False, 78 | "field_11_checkbox_reporting_model_two_ffi": False, 79 | "field_11_checkbox_us_branch": False, 80 | "field_12_disregarded_entity_city": None, 81 | "field_12_disregarded_entity_country": None, 82 | "field_12_disregarded_entity_street": None, 83 | "field_13_disregarded_entity_giin": None, 84 | "field_14a_checkbox": False, 85 | "field_14a_resident_of": None, 86 | "field_14b_checkbox_active_trade_or_business_test": False, 87 | "field_14b_checkbox_benefit_items": False, 88 | "field_14b_checkbox_derivative_benefits_test": False, 89 | "field_14b_checkbox_favorable": False, 90 | "field_14b_checkbox_government": False, 91 | "field_14b_checkbox_no_lob_article_in_treaty": False, 92 | "field_14b_checkbox_other_tax_exempt": False, 93 | "field_14b_checkbox_other": False, 94 | "field_14b_checkbox_ownership_and_base_erosion_test": False, 95 | "field_14b_checkbox_publicly_traded_corporation": False, 96 | "field_14b_checkbox_subsidiary_of_publicly_traded_corporation": False, 97 | "field_14b_checkbox_tax_exempt_pension": False, 98 | "field_14b_other_article": None, 99 | "field_14c_checkbox_dividends": False, 100 | "field_15_special_rates_article": None, 101 | "field_15_special_rates_explanation": None, 102 | "field_15_special_rates_income_type": None, 103 | "field_15_special_rates_percentage": None, 104 | "field_16_name": None, 105 | "field_17a_checkbox": False, 106 | "field_17b_checkbox": False, 107 | "field_18_checkbox": False, 108 | "field_19_checkbox": False, 109 | "field_20_name": None, 110 | "field_21_checkbox": False, 111 | "field_22_checkbox": False, 112 | "field_23_checkbox": False, 113 | "field_24a_checkbox": False, 114 | "field_24b_checkbox": False, 115 | "field_24c_checkbox": False, 116 | "field_24d_checkbox": False, 117 | "field_25a_checkbox": False, 118 | "field_25b_checkbox": False, 119 | "field_25c_checkbox": False, 120 | "field_26_checkbox_model_one": False, 121 | "field_26_checkbox_model_two": False, 122 | "field_26_checkbox_trustee_foreign": False, 123 | "field_26_checkbox_trustee_us": False, 124 | "field_26_checkbox": False, 125 | "field_26_country": None, 126 | "field_26_treated_as": None, 127 | "field_26_trustee_name": None, 128 | "field_27_checkbox": False, 129 | "field_28a_checkbox": False, 130 | "field_28b_checkbox": False, 131 | "field_29a_checkbox": False, 132 | "field_29b_checkbox": False, 133 | "field_29c_checkbox": False, 134 | "field_29d_checkbox": False, 135 | "field_29e_checkbox": False, 136 | "field_29f_checkbox": False, 137 | "field_30_checkbox": False, 138 | "field_31_checkbox": False, 139 | "field_32_checkbox": False, 140 | "field_33_checkbox": False, 141 | "field_33_date": None, 142 | "field_34_checkbox": False, 143 | "field_34_date": None, 144 | "field_35_checkbox": False, 145 | "field_35_date": None, 146 | "field_36_checkbox": False, 147 | "field_37a_checkbox": False, 148 | "field_37a_name": None, 149 | "field_37b_checkbox": False, 150 | "field_37b_market_name": None, 151 | "field_37b_name": None, 152 | "field_38_checkbox": False, 153 | "field_39_checkbox": False, 154 | "field_40a_checkbox": False, 155 | "field_40b_checkbox": False, 156 | "field_40c_checkbox": False, 157 | "field_41_checkbox": False, 158 | "field_42_name": None, 159 | "field_43_checkbox": False, 160 | "passive_nffe_owners": [], 161 | "signature_date": None, 162 | "signature_name": None, 163 | "signed": True, 164 | } 165 | 166 | 167 | @responses.activate 168 | def test_process_w8_url(): 169 | 170 | client = Client(client_id="v", client_secret="w", username="o", api_key="c") 171 | responses.add( 172 | responses.POST, 173 | f"{client.versioned_url}/partner/w-8ben-e/", 174 | json=MOCK, 175 | status=200, 176 | ) 177 | d = client.process_w8_document_url( 178 | file_url="http://cdn-dev.veryfi.com/testing/veryfi-python/receipt_public.jpg" 179 | ) 180 | assert d == MOCK 181 | 182 | 183 | @responses.activate 184 | def test_process_w8(): 185 | 186 | client = Client(client_id="v", client_secret="w", username="o", api_key="c") 187 | responses.add( 188 | responses.POST, 189 | f"{client.versioned_url}/partner/w-8ben-e/", 190 | json=MOCK, 191 | status=200, 192 | ) 193 | d = client.process_w8_document(file_path="tests/assets/receipt_public.jpg") 194 | assert d == MOCK 195 | 196 | 197 | @responses.activate 198 | def test_get_w8s(): 199 | mock = [MOCK] 200 | 201 | client = Client(client_id="v", client_secret="w", username="o", api_key="c") 202 | responses.add( 203 | responses.GET, 204 | f"{client.versioned_url}/partner/w-8ben-e/", 205 | json=mock, 206 | status=200, 207 | ) 208 | d = client.get_w8s() 209 | assert d == mock 210 | -------------------------------------------------------------------------------- /tests/test_w9s.py: -------------------------------------------------------------------------------- 1 | import responses 2 | 3 | from veryfi import Client 4 | 5 | 6 | MOCK = { 7 | "account_numbers": "", 8 | "address1": "28 E 3rd Ave, Suite 201", 9 | "address2": "San Mateo, California, 94401", 10 | "business_name": "", 11 | "c_corp": 0, 12 | "ein": "", 13 | "exempt_payee_code": "", 14 | "exemption": "", 15 | "individual": 0, 16 | "llc": 0, 17 | "name": "Veryfi, Inc.", 18 | "other": 0, 19 | "other_description": "", 20 | "partnership": 0, 21 | "pdf_url": "https://scdn.veryfi.com/w9s/ec278ba0-31d6-4bd4-9d18-cb6a1232788e/output-1.pdf?Expires=1653031170&Signature=bftl34pf~Yni3ysaauqwL4BkfzgMPdAwMpw-SkjKZaxkgSt2~EYmX7NK~BGZ5IFUNdUIGBxTIsBsVWrP8LDQ3fME3kFM6qSn-udZp9Y8WJ-HbqQrIf1DwZQp-A2NSBCkRWgqAtYJo5dQW~UJJdCJx19ZIaYQZzYVQvuHmornzBStTV6D2qXQKUZpv9d5BrvTExZDnIxKy-ibyy09CfUPMc-lsVQLQEb-uQvud-JTf9Guy6k9Y4oT32HSvKcL0pMLvJqYC6mJUM2-5MJiBsYQSNs2e6s8xXcSBotiChMQwBg3RhGv5y-o8Aih1GNmBcvPHJIEyKOuiHeC9TUSELvp~w__&Key-Pair-Id=APKAJCILBXEJFZF4DCHQ", 22 | "requester": "AcMe Corporation 1010 Elm Str,\nMountain View, CA 94043", 23 | "s_corp": 1, 24 | "signature": 1, 25 | "signature_date": "June 19, 2020", 26 | "ssn": "", 27 | "trust_estate": 0, 28 | } 29 | 30 | 31 | @responses.activate 32 | def test_process_w9_document_url(): 33 | 34 | client = Client(client_id="v", client_secret="w", username="o", api_key="c") 35 | responses.add( 36 | responses.POST, 37 | f"{client.versioned_url}/partner/w9s/", 38 | json=MOCK, 39 | status=200, 40 | ) 41 | d = client.process_w9_document_url( 42 | file_url="http://cdn-dev.veryfi.com/testing/veryfi-python/receipt_public.jpg", 43 | ) 44 | assert d == MOCK 45 | 46 | 47 | @responses.activate 48 | def test_process_w9_document(): 49 | 50 | client = Client(client_id="v", client_secret="w", username="o", api_key="c") 51 | responses.add( 52 | responses.POST, 53 | f"{client.versioned_url}/partner/w9s/", 54 | json=MOCK, 55 | status=200, 56 | ) 57 | d = client.process_w9_document(file_path="tests/assets/receipt_public.jpg") 58 | assert d == MOCK 59 | 60 | 61 | @responses.activate 62 | def test_get_w9_documents(): 63 | mock = [MOCK] 64 | client = Client(client_id="v", client_secret="w", username="o", api_key="c") 65 | responses.add( 66 | responses.GET, 67 | f"{client.versioned_url}/partner/w9s/", 68 | json=mock, 69 | status=200, 70 | ) 71 | d = client.get_w9s() 72 | assert d == mock 73 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = py39, py310, py311, py312, report 3 | skipsdist = true 4 | skip_install = true 5 | basepython = py39 6 | 7 | [gh-actions] 8 | python = 9 | 3.9: py39 10 | 3.10: py310 11 | 3.11: py311 12 | 3.12: py312 13 | 14 | [base] 15 | deps = 16 | pip>=21.0.1 17 | pytest 18 | coverage 19 | responses 20 | 21 | 22 | [testenv] 23 | deps = 24 | {[base]deps} 25 | -rrequirements.txt 26 | commands = 27 | {envpython} -m coverage run -m pytest tests --ff {posargs} 28 | 29 | 30 | [testenv:report] 31 | commands = 32 | coverage report -m 33 | 34 | 35 | 36 | [pytest] 37 | filterwarnings = 38 | 39 | 40 | [coverage:run] 41 | branch = True 42 | source = 43 | veryfi 44 | 45 | [coverage:report] 46 | fail_under = 75 47 | precision = 2 48 | skip_covered = True 49 | skip_empty = True 50 | -------------------------------------------------------------------------------- /veryfi/__init__.py: -------------------------------------------------------------------------------- 1 | from veryfi.client import * 2 | -------------------------------------------------------------------------------- /veryfi/_documents/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veryfi/veryfi-python/ebe324aa2471bdac607417f07e7c5d8a779cd201/veryfi/_documents/__init__.py -------------------------------------------------------------------------------- /veryfi/_documents/line_items.py: -------------------------------------------------------------------------------- 1 | from typing import Dict 2 | 3 | from veryfi.client_base import Client 4 | 5 | 6 | class LineItems: 7 | def __init__(self, client: Client): 8 | self.client = client 9 | 10 | def get_line_items(self, document_id: int): 11 | """ 12 | Retrieve all line items for a document. 13 | https://docs.veryfi.com/api/receipts-invoices/get-document-line-items/ 14 | 15 | :param document_id: ID of the document you'd like to retrieve 16 | :return: List of line items extracted from the document 17 | """ 18 | return self.client._request("GET", f"/documents/{document_id}/line-items/") 19 | 20 | def get_line_item(self, document_id: int, line_item_id: int): 21 | """ 22 | Retrieve a line item for existing document by ID. 23 | https://docs.veryfi.com/api/receipts-invoices/get-a-line-item/ 24 | 25 | :param document_id: ID of the document you'd like to retrieve 26 | :param line_item_id: ID of the line item you'd like to retrieve 27 | :return: Line item extracted from the document 28 | """ 29 | return self.client._request("GET", f"/documents/{document_id}/line-items/{line_item_id}") 30 | 31 | def add_line_item(self, document_id: int, payload: Dict) -> Dict: 32 | """ 33 | Add a new line item on an existing document. 34 | https://docs.veryfi.com/api/receipts-invoices/create-a-line-item/ 35 | 36 | :param document_id: ID of the document you'd like to update 37 | :param payload: line item object to add 38 | :return: Added line item data 39 | """ 40 | return self.client._request("POST", f"/documents/{document_id}/line-items/", payload) 41 | 42 | def update_line_item(self, document_id: int, line_item_id: int, payload: Dict) -> Dict: 43 | """ 44 | Update an existing line item on an existing document. 45 | https://docs.veryfi.com/api/receipts-invoices/update-a-line-item/ 46 | 47 | :param document_id: ID of the document you'd like to update 48 | :param line_item_id: ID of the line item you'd like to update 49 | :param payload: line item object to update 50 | :return: Line item data with updated fields, if fields are writable. Otherwise line item data with unchanged fields. 51 | """ 52 | return self.client._request( 53 | "PUT", f"/documents/{document_id}/line-items/{line_item_id}", payload 54 | ) 55 | 56 | def delete_line_items(self, document_id: int): 57 | """ 58 | Delete all line items on an existing document. 59 | https://docs.veryfi.com/api/receipts-invoices/delete-all-document-line-items/ 60 | 61 | :param document_id: ID of the document you'd like to delete 62 | """ 63 | self.client._request("DELETE", f"/documents/{document_id}/line-items/") 64 | 65 | def delete_line_item(self, document_id: int, line_item_id: int): 66 | """ 67 | Delete an existing line item on an existing document. 68 | https://docs.veryfi.com/api/receipts-invoices/delete-a-line-item/ 69 | 70 | :param document_id: ID of the document you'd like to delete 71 | :param line_item_id: ID of the line item you'd like to delete 72 | """ 73 | self.client._request("DELETE", f"/documents/{document_id}/line-items/{line_item_id}") 74 | -------------------------------------------------------------------------------- /veryfi/_documents/pdf_split.py: -------------------------------------------------------------------------------- 1 | import os 2 | import base64 3 | from typing import Dict, List, Optional 4 | 5 | from veryfi.client_base import Client 6 | 7 | 8 | class PDFSplit: 9 | def __init__(self, client: Client): 10 | self.client = client 11 | 12 | def get_pdf(self, **kwargs): 13 | """ 14 | Get a Submitted PDF endpoint allows you to retrieve a collection of previously processed. 15 | https://docs.veryfi.com/api/receipts-invoices/get-submitted-pdf/ 16 | 17 | :param kwargs: Additional query parameters. 18 | :return: The processed Document response. 19 | """ 20 | endpoint_name = "/documents-set/" 21 | return self.client._request("GET", endpoint_name, {}, kwargs) 22 | 23 | def get_documents_from_pdf(self, document_id: int): 24 | """ 25 | Get Documents from PDF endpoint allows you to retrieve a collection of previously processed documents. 26 | https://docs.veryfi.com/api/receipts-invoices/get-documents-from-pdf/ 27 | :param document_id: ID of the document you'd like to retrieve 28 | :return: The processed Document response. 29 | """ 30 | endpoint_name = f"/documents-set/{document_id}" 31 | return self.client._request("GET", endpoint_name, {}) 32 | 33 | def split_and_process_pdf( 34 | self, 35 | file_path: str, 36 | categories: Optional[List] = None, 37 | **kwargs, 38 | ) -> Dict: 39 | """ 40 | Process a document and extract all the fields from it 41 | https://docs.veryfi.com/api/receipts-invoices/split-and-process-a-pdf/ 42 | 43 | :param file_path: Path on disk to a file to submit for data extraction 44 | :param categories: List of categories Veryfi can use to categorize the document 45 | :param kwargs: Additional body parameters 46 | :return: Data extracted from the document 47 | """ 48 | endpoint_name = "/documents-set/" 49 | categories = categories or [] 50 | file_name = os.path.basename(file_path) 51 | with open(file_path, "rb") as image_file: 52 | base64_encoded_string = base64.b64encode(image_file.read()).decode("utf-8") 53 | request_arguments = { 54 | "file_name": file_name, 55 | "file_data": base64_encoded_string, 56 | "categories": categories, 57 | } 58 | request_arguments.update(kwargs) 59 | return self.client._request("POST", endpoint_name, request_arguments) 60 | 61 | def split_and_process_pdf_url( 62 | self, 63 | file_url: Optional[str] = None, 64 | categories: Optional[List[str]] = None, 65 | max_pages_to_process: Optional[int] = None, 66 | file_urls: Optional[List[str]] = None, 67 | **kwargs, 68 | ) -> Dict: 69 | """Process Document from url and extract all the fields from it. 70 | https://docs.veryfi.com/api/receipts-invoices/split-and-process-a-pdf/ 71 | 72 | :param file_url: Required if file_urls isn't specified. Publicly accessible URL to a file, e.g. "https://cdn.example.com/receipt.jpg". 73 | :param file_urls: Required if file_url isn't specifies. List of publicly accessible URLs to multiple files, e.g. ["https://cdn.example.com/receipt1.jpg", "https://cdn.example.com/receipt2.jpg"] 74 | :param categories: List of categories to use when categorizing the document 75 | :param max_pages_to_process: When sending a long document to Veryfi for processing, this parameter controls how many pages of the document will be read and processed, starting from page 1. 76 | :param kwargs: Additional body parameters 77 | :return: Data extracted from the document. 78 | """ 79 | endpoint_name = "/documents-set/" 80 | categories = categories or [] 81 | request_arguments = { 82 | "categories": categories, 83 | "file_url": file_url, 84 | "file_urls": file_urls, 85 | "max_pages_to_process": max_pages_to_process, 86 | } 87 | request_arguments.update(kwargs) 88 | return self.client._request("POST", endpoint_name, request_arguments) 89 | -------------------------------------------------------------------------------- /veryfi/_documents/tags.py: -------------------------------------------------------------------------------- 1 | from veryfi.client_base import Client 2 | 3 | 4 | class Tags: 5 | def __init__(self, client: Client): 6 | self.client = client 7 | 8 | def get_tags(self, document_id): 9 | """ 10 | Return all Tag assigned to a specific Document. 11 | https://docs.veryfi.com/api/receipts-invoices/get-document-tags/ 12 | 13 | :param document_id: ID of the document you'd like to get 14 | :return: Added tags data 15 | """ 16 | endpoint_name = f"/documents/{document_id}/tags" 17 | return self.client._request("GET", endpoint_name, {}) 18 | 19 | def add_tag(self, document_id, tag_name): 20 | """ 21 | Add a new tag on an existing document. 22 | https://docs.veryfi.com/api/receipts-invoices/add-a-tag-to-a-document/ 23 | 24 | :param document_id: ID of the document you'd like to update 25 | :param tag_name: name of the new tag 26 | :return: Added tag data 27 | """ 28 | endpoint_name = f"/documents/{document_id}/tags/" 29 | request_arguments = {"name": tag_name} 30 | return self.client._request("PUT", endpoint_name, request_arguments) 31 | 32 | def replace_tags(self, document_id, tags): 33 | """ 34 | Replace multiple tags on an existing document. 35 | https://docs.veryfi.com/api/receipts-invoices/update-a-document/ 36 | 37 | :param document_id: ID of the document you'd like to update 38 | :param tags: array of strings 39 | :return: Added tags data 40 | """ 41 | endpoint_name = f"/documents/{document_id}/" 42 | request_arguments = {"tags": tags} 43 | return self.client._request("PUT", endpoint_name, request_arguments) 44 | 45 | def add_tags(self, document_id, tags): 46 | """ 47 | Add multiple tags on an existing document. 48 | https://docs.veryfi.com/api/receipts-invoices/add-tags-to-a-document/ 49 | 50 | :param document_id: ID of the document you'd like to update 51 | :param tags: array of strings 52 | :return: Added tags data 53 | """ 54 | endpoint_name = f"/documents/{document_id}/tags/" 55 | request_arguments = {"tags": tags} 56 | return self.client._request("POST", endpoint_name, request_arguments) 57 | 58 | def delete_tag(self, document_id, tag_id): 59 | """ 60 | Unlink a tag from the list of tags assigned to a specific Document. 61 | https://docs.veryfi.com/api/receipts-invoices/unlink-a-tag-from-a-document/ 62 | 63 | :param document_id: ID of the document 64 | :param tag_id: ID of the tag you'd like to unlink 65 | """ 66 | endpoint_name = f"/documents/{document_id}/tags/{tag_id}" 67 | self.client._request("DELETE", endpoint_name, {}) 68 | 69 | def delete_tags(self, document_id): 70 | """ 71 | Unlink all tags assigned to a specific Document. 72 | https://docs.veryfi.com/api/receipts-invoices/unlink-all-tags-from-a-document/ 73 | 74 | :param document_id: ID of the document 75 | """ 76 | endpoint_name = f"/documents/{document_id}/tags" 77 | self.client._request("DELETE", endpoint_name, {}) 78 | -------------------------------------------------------------------------------- /veryfi/_w2s/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veryfi/veryfi-python/ebe324aa2471bdac607417f07e7c5d8a779cd201/veryfi/_w2s/__init__.py -------------------------------------------------------------------------------- /veryfi/_w2s/w2_split.py: -------------------------------------------------------------------------------- 1 | import os 2 | import base64 3 | from typing import Dict, List, Optional 4 | 5 | from veryfi.client_base import Client 6 | 7 | 8 | class W2Split: 9 | def __init__(self, client: Client): 10 | self.client = client 11 | 12 | def get_documents_from_w2(self, document_id: int) -> Dict: 13 | """ 14 | Veryfi's Get Documents from W-2 endpoint allows you to retrieve a collection of previously processed W-2s. 15 | https://docs.veryfi.com/api/get-documents-from-w-2/ 16 | 17 | :param document_id: The unique identifier of the document. 18 | :return: Document Response 19 | """ 20 | endpoint_name = f"/w2s-set/{document_id}/" 21 | return self.client._request("GET", endpoint_name, {}) 22 | 23 | def get_list_of_w2s(self, **kwargs) -> Dict: 24 | """ 25 | Veryfi's Get List of W-2s endpoint allows you to retrieve a collection of previously processed W-2s. 26 | https://docs.veryfi.com/api/get-list-of-documents-from-w-2/ 27 | 28 | :param kwargs: Query parameters 29 | :return: List of W-2s 30 | """ 31 | endpoint_name = "/w2s-set/" 32 | return self.client._request("GET", endpoint_name, {}, kwargs) 33 | 34 | def split_and_process_w2(self, file_path: str, **kwargs) -> Dict: 35 | """ 36 | Process a document and extract all the fields from it 37 | https://docs.veryfi.com/api/split-and-process-a-w-2/ 38 | 39 | :param file_path: Path on disk to a file to submit for data extraction 40 | :param kwargs: Additional body parameters 41 | :return: Data extracted from the document 42 | """ 43 | endpoint_name = "/w2s-set/" 44 | file_name = os.path.basename(file_path) 45 | with open(file_path, "rb") as image_file: 46 | base64_encoded_string = base64.b64encode(image_file.read()).decode("utf-8") 47 | request_arguments = { 48 | "file_name": file_name, 49 | "file_data": base64_encoded_string, 50 | } 51 | request_arguments.update(kwargs) 52 | return self.client._request("POST", endpoint_name, request_arguments) 53 | 54 | def split_and_process_w2_url( 55 | self, 56 | file_url: Optional[str] = None, 57 | file_urls: Optional[List[str]] = None, 58 | max_pages_to_process: Optional[int] = None, 59 | **kwargs, 60 | ) -> Dict: 61 | """Process Document from url and extract all the fields from it. 62 | https://docs.veryfi.com/api/split-and-process-a-w-2/ 63 | 64 | :param file_url: Required if file_urls isn't specified. Publicly accessible URL to a file, e.g. "https://cdn.example.com/receipt.jpg". 65 | :param file_urls: Required if file_url isn't specifies. List of publicly accessible URLs to multiple files, e.g. ["https://cdn.example.com/receipt1.jpg", "https://cdn.example.com/receipt2.jpg"] 66 | :param max_pages_to_process: When sending a long document to Veryfi for processing, this parameter controls how many pages of the document will be read and processed, starting from page 1. 67 | :param kwargs: Additional body parameters 68 | :return: Data extracted from the document. 69 | """ 70 | endpoint_name = "/w2s-set/" 71 | request_arguments = { 72 | "file_url": file_url, 73 | "file_urls": file_urls, 74 | "max_pages_to_process": max_pages_to_process, 75 | } 76 | request_arguments.update(kwargs) 77 | return self.client._request("POST", endpoint_name, request_arguments) 78 | -------------------------------------------------------------------------------- /veryfi/a_docs.py: -------------------------------------------------------------------------------- 1 | import os 2 | import base64 3 | from typing import Dict, List, Optional 4 | 5 | from veryfi.client_base import Client 6 | 7 | 8 | class ADocs: 9 | def __init__(self, client: Client): 10 | self.client = client 11 | 12 | def process_any_document_url( 13 | self, blueprint_name: str, file_url: str, file_name: Optional[str] = None, **kwargs 14 | ) -> Dict: 15 | """ 16 | Process Any Document from url and extract all the fields from it. 17 | https://docs.veryfi.com/api/anydocs/process-A-doc/ 18 | 19 | :param blueprint_name: The blueprint name which was used to extract the data. Same as blueprint_name. 20 | :param file_url: Publicly accessible URL to a file, e.g. "https://cdn.example.com/receipt.jpg". 21 | :param file_name: Optional name of file, eg. receipt.jpg 22 | :param kwargs: Additional body parameters 23 | :return: Data extracted from the document. 24 | """ 25 | if file_name is None: 26 | file_name = os.path.basename(file_url) 27 | endpoint_name = "/any-documents/" 28 | request_arguments = { 29 | "blueprint_name": blueprint_name, 30 | "file_name": file_name, 31 | "file_url": file_url, 32 | } 33 | request_arguments.update(kwargs) 34 | return self.client._request("POST", endpoint_name, request_arguments) 35 | 36 | def process_any_document( 37 | self, blueprint_name: str, file_path: str, file_name: Optional[str] = None, **kwargs 38 | ) -> Dict: 39 | """ 40 | Process Any Document from url and extract all the fields from it. 41 | https://docs.veryfi.com/api/anydocs/process-A-doc/ 42 | 43 | :param blueprint_name: The blueprint name which was used to extract the data. Same as blueprint_name. 44 | :param file_path: Path on disk to a file to submit for data extraction 45 | :param file_name: Optional name of file, eg. receipt.jpg 46 | :param kwargs: Additional body parameters 47 | :return: Data extracted from the document. 48 | """ 49 | endpoint_name = "/any-documents/" 50 | if file_name is None: 51 | file_name = os.path.basename(file_path) 52 | with open(file_path, "rb") as image_file: 53 | base64_encoded_string = base64.b64encode(image_file.read()).decode("utf-8") 54 | request_arguments = { 55 | "blueprint_name": blueprint_name, 56 | "file_name": file_name, 57 | "file_data": base64_encoded_string, 58 | } 59 | 60 | request_arguments.update(kwargs) 61 | return self.client._request("POST", endpoint_name, request_arguments) 62 | 63 | def get_any_document(self, document_id: int, **kwargs) -> Dict: 64 | """ 65 | Get aDocs endpoint allows you to retrieve a previously processed any doc. 66 | https://docs.veryfi.com/api/anydocs/get-a-A-doc/ 67 | 68 | :param document_id: The unique identifier of the document. 69 | :param kwargs: Additional query parameters 70 | :return: Document Data 71 | """ 72 | endpoint_name = f"/any-documents/{document_id}/" 73 | return self.client._request("GET", endpoint_name, {}, kwargs) 74 | 75 | def get_any_documents( 76 | self, 77 | created_date__gt: Optional[str] = None, 78 | created_date__gte: Optional[str] = None, 79 | created_date__lt: Optional[str] = None, 80 | created_date__lte: Optional[str] = None, 81 | **kwargs, 82 | ) -> List[Dict]: 83 | """ 84 | Get a list of documents 85 | https://docs.veryfi.com/api/anydocs/get-A-docs/ 86 | 87 | :param created_date__gt: Search for documents with a created date greater than this one. Format YYYY-MM-DD+HH:MM:SS. Don't send both created_date__gt and created_date__gte in a single request. 88 | :param created_date__gte: Search for documents with a created date greater than or equal to this one. Format YYYY-MM-DD+HH:MM:SS. Don't send both created_date__gt and created_date__gte in a single request. 89 | :param created_date__lt: Search for documents with a created date greater than this one. Format YYYY-MM-DD+HH:MM:SS. Don't send both created_date__lt and created_date__lte in a single request. 90 | :param created_date__lte: Search for documents with a created date less than or equal to this one. Format YYYY-MM-DD+HH:MM:SS. Don't send both created_date__lt and created_date__lte in a single request. 91 | :param kwargs: Additional query parameters 92 | :return: List of previously processed documents 93 | """ 94 | query_params = {} 95 | if created_date__gt: 96 | query_params["created_date__gt"] = created_date__gt 97 | if created_date__gte: 98 | query_params["created_date__gte"] = created_date__gte 99 | if created_date__lt: 100 | query_params["created_date__lt"] = created_date__lt 101 | if created_date__lte: 102 | query_params["created_date__lte"] = created_date__lte 103 | query_params.update(kwargs) 104 | 105 | endpoint_name = "/any-documents/" 106 | return self.client._request("GET", endpoint_name, {}, query_params) 107 | 108 | def delete_any_document(self, document_id: int): 109 | """ 110 | Delete a document. 111 | https://docs.veryfi.com/api/anydocs/delete-a-A-doc/ 112 | 113 | :param document_id: The unique identifier of the document. 114 | """ 115 | endpoint_name = f"/any-documents/{document_id}/" 116 | self.client._request("DELETE", endpoint_name, {}) 117 | -------------------------------------------------------------------------------- /veryfi/bank_statements.py: -------------------------------------------------------------------------------- 1 | import os 2 | import base64 3 | from typing import Dict, Optional 4 | 5 | from veryfi.client_base import Client 6 | 7 | 8 | class BankStatements: 9 | def __init__(self, client: Client): 10 | self.client = client 11 | 12 | def process_bank_statement_document_url( 13 | self, file_url: str, file_name: Optional[str] = None, **kwargs 14 | ) -> Dict: 15 | """ 16 | Process bank statement document from url and extract all the fields from it. 17 | https://docs.veryfi.com/api/bank-statements/process-a-bank-statement/ 18 | 19 | :param file_url: Publicly accessible URL to a file, e.g. "https://cdn.example.com/receipt.jpg". 20 | :param file_name: Optional name of file, eg. receipt.jpg 21 | :param kwargs: Additional body parameters. 22 | :return: Data extracted from the bank statement. 23 | """ 24 | if file_name is None: 25 | file_name = os.path.basename(file_url) 26 | endpoint_name = "/bank-statements/" 27 | request_arguments = { 28 | "file_name": file_name, 29 | "file_url": file_url, 30 | } 31 | request_arguments.update(kwargs) 32 | return self.client._request("POST", endpoint_name, request_arguments) 33 | 34 | def process_bank_statement_document( 35 | self, file_path: str, file_name: Optional[str] = None, **kwargs 36 | ) -> Dict: 37 | """ 38 | Process bank statement document from url and extract all the fields from it. 39 | https://docs.veryfi.com/api/bank-statements/process-a-bank-statement/ 40 | 41 | :param file_path: Path on disk to a file to submit for data extraction 42 | :param file_name: Optional name of file, eg. receipt.jpg 43 | :param kwargs: Additional body parameters. 44 | :return: Data extracted from the bank statement. 45 | """ 46 | endpoint_name = "/bank-statements/" 47 | if file_name is None: 48 | file_name = os.path.basename(file_path) 49 | with open(file_path, "rb") as image_file: 50 | base64_encoded_string = base64.b64encode(image_file.read()).decode("utf-8") 51 | request_arguments = { 52 | "file_name": file_name, 53 | "file_data": base64_encoded_string, 54 | } 55 | request_arguments.update(kwargs) 56 | document = self.client._request("POST", endpoint_name, request_arguments) 57 | return document 58 | 59 | def get_bank_statement(self, document_id: int, **kwargs) -> Dict: 60 | """ 61 | Get bank statement endpoint allows you to retrieve a previously processed bank statement. 62 | https://docs.veryfi.com/api/bank-statements/get-a-bank-statement/ 63 | 64 | :param document_id: The unique identifier of the document. 65 | :param kwargs: Additional query parameters. 66 | :return: Document Data 67 | """ 68 | endpoint_name = f"/bank-statements/{document_id}/" 69 | return self.client._request("GET", endpoint_name, {}, kwargs) 70 | 71 | def get_bank_statements( 72 | self, 73 | created_date__gt: Optional[str] = None, 74 | created_date__gte: Optional[str] = None, 75 | created_date__lt: Optional[str] = None, 76 | created_date__lte: Optional[str] = None, 77 | **kwargs, 78 | ): 79 | """ 80 | Get list of bank statement documents. 81 | https://docs.veryfi.com/api/bank-statements/get-bank-statements/ 82 | 83 | :param created_date__gt: Search for bank statement documents with a created date greater than this one. Format YYYY-MM-DD+HH:MM:SS. Don't send both created__gt and created__gte in a single request. 84 | :param created_date__gte: Search for bank statement documents with a created date greater than or equal to this one. Format YYYY-MM-DD+HH:MM:SS. Don't send both created__gt and created__gte in a single request. 85 | :param created_date__lt: Search for bank statement documents with a created date greater than this one. Format YYYY-MM-DD+HH:MM:SS. Don't send both created__lt and created__lte in a single request. 86 | :param created_date__lte: Search for bank statement documents with a created date less than or equal to this one. Format YYYY-MM-DD+HH:MM:SS. Don't send both created__lt and created__lte in a single request. 87 | :param kwargs: Additional query parameters. 88 | :return: List of previously processed documents 89 | """ 90 | 91 | query_params = {} 92 | if created_date__gt: 93 | query_params["created_date__gt"] = created_date__gt 94 | if created_date__gte: 95 | query_params["created_date__gte"] = created_date__gte 96 | if created_date__lt: 97 | query_params["created_date__lt"] = created_date__lt 98 | if created_date__lte: 99 | query_params["created_date__lte"] = created_date__lte 100 | query_params.update(kwargs) 101 | 102 | endpoint_name = "/bank-statements/" 103 | return self.client._request("GET", endpoint_name, {}, query_params) 104 | 105 | def delete_bank_statement(self, document_id: int): 106 | """ 107 | Delete a bank statement document. 108 | https://docs.veryfi.com/api/bank-statements/delete-a-bank-statement/ 109 | 110 | :param document_id: The unique identifier of the document. 111 | """ 112 | 113 | endpoint_name = f"/bank-statements/{document_id}/" 114 | self.client._request("DELETE", endpoint_name, {}) 115 | -------------------------------------------------------------------------------- /veryfi/bussines_cards.py: -------------------------------------------------------------------------------- 1 | import os 2 | import base64 3 | from typing import Dict, Optional 4 | 5 | from veryfi.client_base import Client 6 | 7 | 8 | class BussinesCards: 9 | def __init__(self, client: Client): 10 | self.client = client 11 | 12 | def process_bussines_card_document_url( 13 | self, file_url: str, file_name: Optional[str] = None, **kwargs 14 | ) -> Dict: 15 | """ 16 | Process bussiness card from url and extract all the fields from it. 17 | https://docs.veryfi.com/api/business-cards/process-a-business-card/ 18 | 19 | :param file_url: Publicly accessible URL to a file, e.g. "https://cdn.example.com/receipt.jpg". 20 | :param file_name: Optional name of file, eg. receipt.jpg 21 | :param kwargs: Additional body parameters 22 | :return: Data extracted from the business card. 23 | """ 24 | if file_name is None: 25 | file_name = os.path.basename(file_url) 26 | endpoint_name = "/business-cards/" 27 | request_arguments = { 28 | "file_name": file_name, 29 | "file_url": file_url, 30 | } 31 | request_arguments.update(kwargs) 32 | return self.client._request("POST", endpoint_name, request_arguments) 33 | 34 | def process_bussines_card_document( 35 | self, file_path: str, file_name: Optional[str] = None, **kwargs 36 | ) -> Dict: 37 | """ 38 | Process bussiness card from url and extract all the fields from it. 39 | https://docs.veryfi.com/api/business-cards/process-a-business-card/ 40 | 41 | :param file_path: Path on disk to a file to submit for data extraction 42 | :param file_name: Optional name of file, eg. receipt.jpg 43 | :param kwargs: Additional body parameters 44 | :return: Data extracted from the business card. 45 | """ 46 | endpoint_name = "/business-cards/" 47 | if file_name is None: 48 | file_name = os.path.basename(file_path) 49 | with open(file_path, "rb") as image_file: 50 | base64_encoded_string = base64.b64encode(image_file.read()).decode("utf-8") 51 | request_arguments = { 52 | "file_name": file_name, 53 | "file_data": base64_encoded_string, 54 | } 55 | request_arguments.update(kwargs) 56 | return self.client._request("POST", endpoint_name, request_arguments) 57 | 58 | def get_business_cards(self, **kwargs): 59 | """ 60 | Get list of business card documents. 61 | https://docs.veryfi.com/api/business-cards/get-business-cards/ 62 | 63 | :param kwargs: Additional query parameters 64 | :return: List of previously processed documents 65 | """ 66 | endpoint_name = "/business-cards/" 67 | return self.client._request("GET", endpoint_name, {}, kwargs) 68 | 69 | def get_business_card(self, document_id: int, **kwargs) -> Dict: 70 | """ 71 | Get a business card document. 72 | https://docs.veryfi.com/api/business-cards/get-a-business-card/ 73 | 74 | :param document_id: The unique identifier of the document. 75 | :param kwargs: Additional query parameters 76 | """ 77 | endpoint_name = f"/business-cards/{document_id}/" 78 | return self.client._request("GET", endpoint_name, {}, kwargs) 79 | 80 | def delete_business_card(self, document_id: int): 81 | """ 82 | Delete a business card document. 83 | https://docs.veryfi.com/api/business-cards/delete-a-business-card/ 84 | 85 | :param document_id: The unique identifier of the document. 86 | """ 87 | endpoint_name = f"/business-cards/{document_id}/" 88 | self.client._request("DELETE", endpoint_name, {}) 89 | -------------------------------------------------------------------------------- /veryfi/checks.py: -------------------------------------------------------------------------------- 1 | import os 2 | import base64 3 | from typing import Dict, List, Optional 4 | 5 | from veryfi.client_base import Client 6 | 7 | 8 | class Checks: 9 | def __init__(self, client: Client): 10 | self.client = client 11 | 12 | def get_checks( 13 | self, 14 | created_date__gt: Optional[str] = None, 15 | created_date__gte: Optional[str] = None, 16 | created_date__lt: Optional[str] = None, 17 | created_date__lte: Optional[str] = None, 18 | **kwargs, 19 | ): 20 | """ 21 | Get list of checks 22 | https://docs.veryfi.com/api/checks/get-checks/ 23 | 24 | :param created_date__gt: Search for checks documents with a created date greater than this one. Format YYYY-MM-DD+HH:MM:SS. Don't send both created_date__gt and created_date__gte in a single request. 25 | :param created_date__gte: Search for checks documents with a created date greater than or equal to this one. Format YYYY-MM-DD+HH:MM:SS. Don't send both created_date__gt and created_date__gte in a single request. 26 | :param created_date__lt: Search for checks documents with a created date greater than this one. Format YYYY-MM-DD+HH:MM:SS. Don't send both created_date__lt and created_date__lte in a single request. 27 | :param created_date__lte: Search for checks documents with a created date less than or equal to this one. Format YYYY-MM-DD+HH:MM:SS. Don't send both created_date__lt and created_date__lte in a single request. 28 | :param kwargs: Additional query parameters 29 | :return: List of previously processed documents 30 | """ 31 | 32 | query_params = {} 33 | if created_date__gt: 34 | query_params["created_date__gt"] = created_date__gt 35 | if created_date__gte: 36 | query_params["created_date__gte"] = created_date__gte 37 | if created_date__lt: 38 | query_params["created_date__lt"] = created_date__lt 39 | if created_date__lte: 40 | query_params["created_date__lte"] = created_date__lte 41 | query_params.update(kwargs) 42 | 43 | endpoint_name = "/checks/" 44 | return self.client._request("GET", endpoint_name, {}, query_params) 45 | 46 | def get_check(self, document_id: int, **kwargs) -> Dict: 47 | """ 48 | Retrieve a check document by ID 49 | https://docs.veryfi.com/api/checks/get-a-check/ 50 | 51 | :param document_id: ID of the document you'd like to retrieve 52 | :param kwargs: Additional query parameters 53 | :return: Data extracted from the Document 54 | """ 55 | endpoint_name = f"/checks/{document_id}/" 56 | return self.client._request("GET", endpoint_name, {}, kwargs) 57 | 58 | def process_check( 59 | self, 60 | file_path: str, 61 | **kwargs, 62 | ) -> Dict: 63 | """ 64 | Process a check document and extract all the fields from it 65 | https://docs.veryfi.com/api/checks/process-a-check/ 66 | 67 | :param file_path: Path on disk to a file to submit for data extraction 68 | :param kwargs: Additional body parameters 69 | :return: Data extracted from the document 70 | """ 71 | endpoint_name = "/checks/" 72 | file_name = os.path.basename(file_path) 73 | with open(file_path, "rb") as image_file: 74 | base64_encoded_string = base64.b64encode(image_file.read()).decode("utf-8") 75 | request_arguments = { 76 | "file_name": file_name, 77 | "file_data": base64_encoded_string, 78 | } 79 | request_arguments.update(kwargs) 80 | return self.client._request("POST", endpoint_name, request_arguments) 81 | 82 | def process_check_url( 83 | self, 84 | file_url: Optional[str] = None, 85 | file_urls: Optional[List[str]] = None, 86 | **kwargs, 87 | ) -> Dict: 88 | """ 89 | Process a check document from url and extract all the fields from it. 90 | https://docs.veryfi.com/api/checks/process-a-check/ 91 | 92 | :param file_url: Required if file_urls isn't specified. Publicly accessible URL to a file, e.g. "https://cdn.example.com/receipt.jpg". 93 | :param file_urls: Required if file_url isn't specifies. List of publicly accessible URLs to multiple files, e.g. ["https://cdn.example.com/receipt1.jpg", "https://cdn.example.com/receipt2.jpg"] 94 | :param kwargs: Additional body parameters. 95 | :return: Data extracted from the document. 96 | """ 97 | endpoint_name = "/checks/" 98 | request_arguments = { 99 | "file_url": file_url, 100 | "file_urls": file_urls, 101 | } 102 | request_arguments.update(kwargs) 103 | return self.client._request("POST", endpoint_name, request_arguments) 104 | 105 | def process_check_with_remittance(self, file_path: str, **kwargs) -> Dict: 106 | """ 107 | Process a check document with remittance and extract all the fields from it 108 | https://docs.veryfi.com/api/checks/process-a-check-with-remittance/ 109 | 110 | :param file_path: Path on disk to a file to submit for data extraction 111 | :param kwargs: Additional body parameters 112 | :return: Data extracted from the document and check 113 | """ 114 | endpoint_name = "/check-with-document/" 115 | file_name = os.path.basename(file_path) 116 | with open(file_path, "rb") as image_file: 117 | base64_encoded_string = base64.b64encode(image_file.read()).decode("utf-8") 118 | request_arguments = { 119 | "file_name": file_name, 120 | "file_data": base64_encoded_string, 121 | } 122 | request_arguments.update(kwargs) 123 | return self.client._request("POST", endpoint_name, request_arguments) 124 | 125 | def process_check_with_remittance_url( 126 | self, 127 | file_url: str, 128 | file_urls: Optional[List[str]] = None, 129 | **kwargs, 130 | ) -> Dict: 131 | """ 132 | Process a check document with remittance from url and extract all the fields from it 133 | https://docs.veryfi.com/api/checks/process-a-check-with-remittance/ 134 | 135 | :param file_url: Publicly accessible URL to a file, e.g. "https://cdn.example.com/receipt.jpg". 136 | :param file_urls: Optional list of publicly accessible URLs to multiple files, e.g. ["https://cdn.example.com/receipt1.jpg", "https://cdn.example.com/receipt2.jpg"] 137 | :param kwargs: Additional body parameters 138 | :return: Data extracted from the document and check 139 | """ 140 | endpoint_name = "/check-with-document/" 141 | request_arguments = { 142 | "file_url": file_url, 143 | "file_urls": file_urls, 144 | } 145 | request_arguments.update(kwargs) 146 | return self.client._request("POST", endpoint_name, request_arguments) 147 | 148 | def update_check(self, document_id: int, **kwargs) -> Dict: 149 | """ 150 | Update data for a previously processed document, including almost any field like `vendor`, `date`, `notes` and etc. 151 | https://docs.veryfi.com/api/checks/update-a-check/ 152 | 153 | ```veryfi_client.update_check(id, date="2021-01-01", notes="look what I did")``` 154 | 155 | :param document_id: ID of the document you'd like to update 156 | :param kwargs: fields to update 157 | :return: A document json with updated fields, if fields are writable. Otherwise a document with unchanged fields. 158 | """ 159 | 160 | endpoint_name = f"/checks/{document_id}/" 161 | return self.client._request("PUT", endpoint_name, kwargs) 162 | 163 | def delete_check(self, document_id: int): 164 | """ 165 | Delete a check document from Veryfi 166 | https://docs.veryfi.com/api/checks/delete-a-check/ 167 | 168 | :param document_id: ID of the check document you'd like to delete 169 | """ 170 | endpoint_name = f"/checks/{document_id}/" 171 | self.client._request("DELETE", endpoint_name, {}) 172 | -------------------------------------------------------------------------------- /veryfi/client.py: -------------------------------------------------------------------------------- 1 | from veryfi.client_base import Client as ClientBase 2 | 3 | from veryfi.a_docs import ADocs 4 | from veryfi.bank_statements import BankStatements 5 | from veryfi.bussines_cards import BussinesCards 6 | from veryfi.checks import Checks 7 | from veryfi.documents import Documents 8 | from veryfi.w2s import W2s 9 | from veryfi.w8s import W8s 10 | from veryfi.w9s import W9s 11 | 12 | 13 | class Client(ClientBase, ADocs, BankStatements, BussinesCards, Checks, Documents, W2s, W8s, W9s): 14 | def __init__( 15 | self, 16 | client_id: str, 17 | client_secret: str, 18 | username: str, 19 | api_key: str, 20 | base_url: str = ClientBase.BASE_URL, 21 | api_version: str = ClientBase.API_VERSION, 22 | timeout: int = ClientBase.API_TIMEOUT, 23 | ): 24 | super().__init__( 25 | client_id=client_id, 26 | client_secret=client_secret, 27 | username=username, 28 | api_key=api_key, 29 | base_url=base_url, 30 | api_version=api_version, 31 | timeout=timeout, 32 | ) 33 | ADocs.__init__(self, super()) 34 | BankStatements.__init__(self, super()) 35 | BussinesCards.__init__(self, super()) 36 | Checks.__init__(self, super()) 37 | Documents.__init__(self, super()) 38 | W2s.__init__(self, super()) 39 | W8s.__init__(self, super()) 40 | W9s.__init__(self, super()) 41 | -------------------------------------------------------------------------------- /veryfi/client_base.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import base64 3 | import hashlib 4 | import hmac 5 | import json 6 | import time 7 | from typing import Dict, Optional 8 | 9 | from veryfi.errors import VeryfiClientError 10 | 11 | 12 | class Client: 13 | 14 | API_VERSION = "v8" 15 | API_TIMEOUT = 30 16 | BASE_URL = "https://api.veryfi.com/api/" 17 | 18 | def __init__( 19 | self, 20 | client_id: str, 21 | client_secret: str, 22 | username: str, 23 | api_key: str, 24 | base_url: str = BASE_URL, 25 | api_version: str = API_VERSION, 26 | timeout: int = API_TIMEOUT, 27 | ): 28 | self.client_id = client_id 29 | self.client_secret = client_secret 30 | self.username = username 31 | self.api_key = api_key 32 | self.base_url = base_url 33 | self.api_version = api_version 34 | self.versioned_url = self.base_url + self.api_version 35 | self.timeout = timeout 36 | self.headers = {} 37 | self._session = requests.Session() 38 | 39 | def _get_headers(self) -> Dict: 40 | """ 41 | Prepares the headers needed for a request. 42 | :return: Dictionary with headers 43 | """ 44 | final_headers = { 45 | "User-Agent": "Python Veryfi-Python/5.0.0", 46 | "Accept": "application/json", 47 | "Content-Type": "application/json", 48 | "Client-Id": self.client_id, 49 | } 50 | 51 | final_headers.update({"Authorization": f"apikey {self.username}:{self.api_key}"}) 52 | 53 | return final_headers 54 | 55 | def _request( 56 | self, 57 | http_verb: str, 58 | endpoint_name: str, 59 | request_arguments: Optional[Dict] = None, 60 | query_params: Optional[Dict] = None, 61 | ): 62 | """ 63 | Submit the HTTP request. 64 | :param http_verb: HTTP Method 65 | :param endpoint_name: Endpoint name such as 'documents', 'users', etc. 66 | :param request_arguments: JSON payload to send to Veryfi 67 | :return: A JSON of the response data. 68 | """ 69 | headers = self._get_headers() 70 | api_url = f"{self.versioned_url}/partner{endpoint_name}" 71 | request_arguments = request_arguments or {} 72 | 73 | if self.client_secret: 74 | timestamp = int(time.time() * 1000) 75 | signature = self._generate_signature(request_arguments, timestamp=timestamp) 76 | headers.update( 77 | { 78 | "X-Veryfi-Request-Timestamp": str(timestamp), 79 | "X-Veryfi-Request-Signature": signature, 80 | } 81 | ) 82 | 83 | response = self._session.request( 84 | http_verb, 85 | url=api_url, 86 | params=query_params or None, 87 | headers=headers, 88 | data=json.dumps(request_arguments), 89 | timeout=self.timeout, 90 | ) 91 | 92 | if response.status_code not in [200, 201, 202, 204]: 93 | raise VeryfiClientError.from_response(response) 94 | 95 | return response.json() 96 | 97 | def _generate_signature(self, payload_params: Dict, timestamp: int) -> str: 98 | """ 99 | Generate unique signature for payload params. 100 | :param payload_params: JSON params to be sent to API request 101 | :param timestamp: Unix Long timestamp 102 | :return: Unique signature generated using the client_secret and the payload 103 | """ 104 | payload = f"timestamp:{timestamp}" 105 | for key in payload_params.keys(): 106 | value = payload_params[key] 107 | payload = f"{payload},{key}:{value}" 108 | 109 | secret_bytes = bytes(self.client_secret, "utf-8") 110 | payload_bytes = bytes(payload, "utf-8") 111 | tmp_signature = hmac.new(secret_bytes, msg=payload_bytes, digestmod=hashlib.sha256).digest() 112 | base64_signature = base64.b64encode(tmp_signature).decode("utf-8").strip() 113 | return base64_signature 114 | -------------------------------------------------------------------------------- /veryfi/documents.py: -------------------------------------------------------------------------------- 1 | import os 2 | import base64 3 | from typing import Dict, List, Optional 4 | 5 | from veryfi._documents.line_items import LineItems 6 | from veryfi._documents.tags import Tags 7 | from veryfi._documents.pdf_split import PDFSplit 8 | from veryfi.client_base import Client 9 | 10 | 11 | class Documents(Tags, LineItems, PDFSplit): 12 | 13 | DEFAULT_CATEGORIES = [ 14 | "Advertising & Marketing", 15 | "Automotive", 16 | "Bank Charges & Fees", 17 | "Legal & Professional Services", 18 | "Insurance", 19 | "Meals & Entertainment", 20 | "Office Supplies & Software", 21 | "Taxes & Licenses", 22 | "Travel", 23 | "Rent & Lease", 24 | "Repairs & Maintenance", 25 | "Payroll", 26 | "Utilities", 27 | "Job Supplies", 28 | "Grocery", 29 | ] 30 | 31 | def __init__(self, client: Client): 32 | self.client = client 33 | LineItems.__init__(self, self.client) 34 | Tags.__init__(self, self.client) 35 | PDFSplit.__init__(self, self.client) 36 | 37 | def get_documents( 38 | self, 39 | q: Optional[str] = None, 40 | external_id: Optional[str] = None, 41 | tag: Optional[str] = None, 42 | created_gt: Optional[str] = None, 43 | created_gte: Optional[str] = None, 44 | created_lt: Optional[str] = None, 45 | created_lte: Optional[str] = None, 46 | **kwargs, 47 | ) -> Dict: 48 | """ 49 | Get list of documents. 50 | https://docs.veryfi.com/api/receipts-invoices/search-documents/ 51 | 52 | :param q: Search query 53 | :param external_id: Search by external ID 54 | :param tag: Search by tag 55 | :param created_gt: Search by created date greater than 56 | :param created_gte: Search by created date greater than or equal to 57 | :param created_lt: Search by created date less than 58 | :param created_lte: Search by created date less than or equal to 59 | :param kwargs: Additional query parameters 60 | :return: List of previously processed documents 61 | """ 62 | query_params = {} 63 | if q: 64 | query_params["q"] = q 65 | if external_id: 66 | query_params["external_id"] = external_id 67 | if tag: 68 | query_params["tag"] = tag 69 | if created_gt: 70 | query_params["created__gt"] = created_gt 71 | if created_gte: 72 | query_params["created__gte"] = created_gte 73 | if created_lt: 74 | query_params["created__lt"] = created_lt 75 | if created_lte: 76 | query_params["created__lte"] = created_lte 77 | query_params.update(kwargs) 78 | 79 | endpoint_name = "/documents/" 80 | return self.client._request("GET", endpoint_name, {}, query_params) 81 | 82 | def get_document(self, document_id: int, **kwargs) -> Dict: 83 | """ 84 | Retrieve document by ID 85 | https://docs.veryfi.com/api/receipts-invoices/get-a-document/ 86 | 87 | :param document_id: ID of the document you'd like to retrieve 88 | :param kwargs: Additional query parameters 89 | :return: Data extracted from the Document 90 | """ 91 | endpoint_name = f"/documents/{document_id}/" 92 | return self.client._request("GET", endpoint_name, {}, kwargs) 93 | 94 | def process_document( 95 | self, 96 | file_path: str, 97 | categories: Optional[List] = None, 98 | delete_after_processing: bool = False, 99 | **kwargs, 100 | ) -> Dict: 101 | """ 102 | Process a document and extract all the fields from it. 103 | https://docs.veryfi.com/api/receipts-invoices/process-a-document/ 104 | 105 | :param file_path: Path on disk to a file to submit for data extraction 106 | :param categories: List of categories Veryfi can use to categorize the document 107 | :param delete_after_processing: Delete this document from Veryfi after data has been extracted 108 | :param kwargs: Additional body parameters 109 | :return: Data extracted from the document 110 | """ 111 | if not categories: 112 | categories = self.DEFAULT_CATEGORIES 113 | file_name = os.path.basename(file_path) 114 | with open(file_path, "rb") as image_file: 115 | base64_encoded_string = base64.b64encode(image_file.read()).decode("utf-8") 116 | request_arguments = { 117 | "file_name": file_name, 118 | "file_data": base64_encoded_string, 119 | "categories": categories, 120 | "auto_delete": delete_after_processing, 121 | } 122 | request_arguments.update(kwargs) 123 | return self.client._request("POST", "/documents/", request_arguments) 124 | 125 | def process_document_url( 126 | self, 127 | file_url: Optional[str] = None, 128 | categories: Optional[List[str]] = None, 129 | delete_after_processing: bool = False, 130 | boost_mode: bool = False, 131 | external_id: Optional[str] = None, 132 | max_pages_to_process: Optional[int] = None, 133 | file_urls: Optional[List[str]] = None, 134 | **kwargs, 135 | ) -> Dict: 136 | """Process Document from url and extract all the fields from it. 137 | https://docs.veryfi.com/api/receipts-invoices/process-a-document/ 138 | 139 | :param file_url: Required if file_urls isn't specified. Publicly accessible URL to a file, e.g. "https://cdn.example.com/receipt.jpg". 140 | :param file_urls: Required if file_url isn't specifies. List of publicly accessible URLs to multiple files, e.g. ["https://cdn.example.com/receipt1.jpg", "https://cdn.example.com/receipt2.jpg"] 141 | :param categories: List of categories to use when categorizing the document 142 | :param delete_after_processing: Delete this document from Veryfi after data has been extracted 143 | :param max_pages_to_process: When sending a long document to Veryfi for processing, this parameter controls how many pages of the document will be read and processed, starting from page 1. 144 | :param boost_mode: Flag that tells Veryfi whether boost mode should be enabled. When set to 1, Veryfi will skip data enrichment steps, but will process the document faster. Default value for this flag is 0 145 | :param external_id: Optional custom document identifier. Use this if you would like to assign your own ID to documents 146 | :param kwargs: Additional body parameters 147 | :return: Data extracted from the document. 148 | """ 149 | endpoint_name = "/documents/" 150 | request_arguments = { 151 | "auto_delete": delete_after_processing, 152 | "boost_mode": boost_mode, 153 | "categories": categories, 154 | "external_id": external_id, 155 | "file_url": file_url, 156 | "file_urls": file_urls, 157 | "max_pages_to_process": max_pages_to_process, 158 | } 159 | request_arguments.update(kwargs) 160 | return self.client._request("POST", endpoint_name, request_arguments) 161 | 162 | def process_documents_bulk(self, file_urls: List[str]) -> List[int]: 163 | """ 164 | Process multiple documents from urls and extract all the fields from it. 165 | If you want to use this endpoint, please contact support@veryfi.com first. Veryfi's Bulk upload allows you to process multiple Documents. 166 | https://docs.veryfi.com/api/receipts-invoices/bulk-process-multiple-documents/ 167 | 168 | :param file_urls: List of publicly accessible URLs to multiple files, e.g. ["https://cdn.example.com/receipt1.jpg", "https://cdn.example.com/receipt2.jpg"] 169 | :return: List of document IDs being processed 170 | """ 171 | endpoint_name = "/documents/bulk/" 172 | request_arguments = {"file_urls": file_urls} 173 | return self.client._request("POST", endpoint_name, request_arguments) 174 | 175 | def delete_document(self, document_id: int): 176 | """ 177 | Delete Document from Veryfi 178 | https://docs.veryfi.com/api/receipts-invoices/delete-a-document/ 179 | 180 | :param document_id: ID of the document you'd like to delete 181 | """ 182 | self.client._request("DELETE", f"/documents/{document_id}/", {"id": document_id}) 183 | 184 | def update_document(self, document_id: int, **kwargs) -> Dict: 185 | """ 186 | Update data for a previously processed document, including almost any field like `vendor`, `date`, `notes` and etc. 187 | https://docs.veryfi.com/api/receipts-invoices/update-a-document/ 188 | 189 | ```veryfi_client.update_document(id, date="2021-01-01", notes="look what I did")``` 190 | 191 | :param document_id: ID of the document you'd like to update 192 | :param kwargs: fields to update 193 | :return: A document json with updated fields, if fields are writable. Otherwise a document with unchanged fields. 194 | """ 195 | return self.client._request("PUT", f"/documents/{document_id}/", kwargs) 196 | -------------------------------------------------------------------------------- /veryfi/errors.py: -------------------------------------------------------------------------------- 1 | class VeryfiClientError(Exception): 2 | optional_fields = ["error", "code"] 3 | 4 | def __init__(self, raw_response, **error_info): 5 | for field_name in self.optional_fields: 6 | setattr(self, field_name, error_info.get(field_name)) 7 | 8 | self.raw_response = raw_response 9 | self.status = raw_response.status_code 10 | 11 | if getattr(self, "error"): 12 | super().__init__(f"{self.status}, {getattr(self, 'error')}") 13 | 14 | @staticmethod 15 | def from_response(raw_response): 16 | """Veryfi API returns error messages with a json body 17 | like: 18 | { 19 | 'status': 'fail', 20 | 'error': 'Human readable error description.' 21 | } 22 | """ 23 | error_cls = _error_map.get(raw_response.status_code) or VeryfiClientError 24 | return error_cls(raw_response, **raw_response.json()) 25 | 26 | 27 | class UnauthorizedAccessToken(VeryfiClientError): 28 | pass 29 | 30 | 31 | class BadRequest(VeryfiClientError): 32 | pass 33 | 34 | 35 | class ResourceNotFound(VeryfiClientError): 36 | pass 37 | 38 | 39 | class UnexpectedHTTPMethod(VeryfiClientError): 40 | pass 41 | 42 | 43 | class AccessLimitReached(VeryfiClientError): 44 | pass 45 | 46 | 47 | class InternalError(VeryfiClientError): 48 | pass 49 | 50 | 51 | class ServiceUnavailable(VeryfiClientError): 52 | pass 53 | 54 | 55 | _error_map = { 56 | 400: BadRequest, 57 | 404: ResourceNotFound, 58 | 401: UnauthorizedAccessToken, 59 | 405: UnexpectedHTTPMethod, 60 | 409: AccessLimitReached, 61 | 500: InternalError, 62 | 503: ServiceUnavailable, 63 | } 64 | -------------------------------------------------------------------------------- /veryfi/w2s.py: -------------------------------------------------------------------------------- 1 | import os 2 | import base64 3 | from typing import Dict, Optional 4 | 5 | from veryfi.client_base import Client 6 | from veryfi._w2s.w2_split import W2Split 7 | 8 | 9 | class W2s(W2Split): 10 | def __init__(self, client: Client): 11 | self.client = client 12 | 13 | def process_w2_document_url( 14 | self, file_url: str, file_name: Optional[str] = None, **kwargs 15 | ) -> Dict: 16 | """ 17 | Process W2 Document from url and extract all the fields from it. 18 | https://docs.veryfi.com/api/w2s/process-a-w-2/ 19 | 20 | :param file_url: Publicly accessible URL to a file, e.g. "https://cdn.example.com/receipt.jpg". 21 | :param file_name: Optional name of file, eg. receipt.jpg 22 | :param kwargs: Additional body parameters 23 | :return: Data extracted from the w2. 24 | """ 25 | if file_name is None: 26 | file_name = os.path.basename(file_url) 27 | endpoint_name = "/w2s/" 28 | request_arguments = { 29 | "file_name": file_name, 30 | "file_url": file_url, 31 | } 32 | request_arguments.update(kwargs) 33 | return self.client._request("POST", endpoint_name, request_arguments) 34 | 35 | def process_w2_document( 36 | self, file_path: str, file_name: Optional[str] = None, **kwargs 37 | ) -> Dict: 38 | """ 39 | Process W2 Document from url and extract all the fields from it. 40 | https://docs.veryfi.com/api/w2s/process-a-w-2/ 41 | 42 | :param file_path: Path on disk to a file to submit for data extraction 43 | :param file_name: Optional name of file, eg. receipt.jpg 44 | :param kwargs: Additional body parameters 45 | :return: Data extracted from the w2. 46 | """ 47 | endpoint_name = "/w2s/" 48 | if file_name is None: 49 | file_name = os.path.basename(file_path) 50 | with open(file_path, "rb") as image_file: 51 | base64_encoded_string = base64.b64encode(image_file.read()).decode("utf-8") 52 | request_arguments = { 53 | "file_name": file_name, 54 | "file_data": base64_encoded_string, 55 | } 56 | request_arguments.update(kwargs) 57 | return self.client._request("POST", endpoint_name, request_arguments) 58 | 59 | def get_w2(self, document_id: int, **kwargs) -> Dict: 60 | """ 61 | Get W-2 endpoint allows you to retrieve a previously processed W-2 62 | https://docs.veryfi.com/api/w2s/get-a-w-2/ 63 | 64 | :param document_id: The unique identifier of the document. 65 | :param kwargs: Additional query parameters 66 | :return: Document Data 67 | """ 68 | endpoint_name = f"/w2s/{document_id}/" 69 | return self.client._request("GET", endpoint_name, {}, kwargs) 70 | 71 | def get_w2s( 72 | self, 73 | created_date_gt: Optional[str] = None, 74 | created_date_gte: Optional[str] = None, 75 | created_date_lt: Optional[str] = None, 76 | created_date_lte: Optional[str] = None, 77 | **kwargs: Dict, 78 | ): 79 | """ 80 | Get list of w2s documents. 81 | https://docs.veryfi.com/api/w2s/get-w-2-s/ 82 | 83 | :param created_date__gt: Search for w2s documents with a created date greater than this one. Format YYYY-MM-DD+HH:MM:SS. Don't send both created_date__gt and created_date__gte in a single request. 84 | :param created_date__gte: Search for w2s documents with a created date greater than or equal to this one. Format YYYY-MM-DD+HH:MM:SS. Don't send both created_date__gt and created_date__gte in a single request. 85 | :param created_date__lt: Search for w2s documents with a created date greater than this one. Format YYYY-MM-DD+HH:MM:SS. Don't send both created_date__lt and created_date__lte in a single request. 86 | :param created_date__lte: Search for w2s documents with a created date less than or equal to this one. Format YYYY-MM-DD+HH:MM:SS. Don't send both created_date__lt and created_date__lte in a single request. 87 | :param kwargs: Additional query parameters 88 | :return: List of previously processed documents 89 | """ 90 | query_params = {} 91 | if created_date_gt: 92 | query_params["created_date__gt"] = created_date_gt 93 | if created_date_gte: 94 | query_params["created_date__gte"] = created_date_gte 95 | if created_date_lt: 96 | query_params["created_date__lt"] = created_date_lt 97 | if created_date_lte: 98 | query_params["created_date__lte"] = created_date_lte 99 | query_params.update(kwargs) 100 | 101 | endpoint_name = "/w2s/" 102 | return self.client._request("GET", endpoint_name, {}, query_params) 103 | 104 | def delete_w2(self, document_id: int): 105 | """ 106 | Delete a w2 document. 107 | https://docs.veryfi.com/api/w2s/delete-a-w-2/ 108 | """ 109 | endpoint_name = f"/w2s/{document_id}/" 110 | self.client._request("DELETE", endpoint_name, {}) 111 | -------------------------------------------------------------------------------- /veryfi/w8s.py: -------------------------------------------------------------------------------- 1 | import os 2 | import base64 3 | from typing import Dict, Optional 4 | 5 | from veryfi.client_base import Client 6 | 7 | 8 | class W8s: 9 | def __init__(self, client: Client): 10 | self.client = client 11 | 12 | def process_w8_document_url( 13 | self, file_url: str, file_name: Optional[str] = None, **kwargs 14 | ) -> Dict: 15 | """ 16 | Process W2 Document from url and extract all the fields from it. 17 | https://docs.veryfi.com/api/w-8ben-e/process-a-w-8-ben-e/ 18 | 19 | :param file_url: Publicly accessible URL to a file, e.g. "https://cdn.example.com/receipt.jpg". 20 | :param file_name: Optional name of file, eg. receipt.jpg 21 | :param kwargs: Additional body parameters 22 | :return: Data extracted from the w8. 23 | """ 24 | if file_name is None: 25 | file_name = os.path.basename(file_url) 26 | endpoint_name = "/w-8ben-e/" 27 | request_arguments = { 28 | "file_name": file_name, 29 | "file_url": file_url, 30 | } 31 | request_arguments.update(kwargs) 32 | return self.client._request("POST", endpoint_name, request_arguments) 33 | 34 | def process_w8_document( 35 | self, file_path: str, file_name: Optional[str] = None, **kwargs 36 | ) -> Dict: 37 | """ 38 | Process W8 Document from url and extract all the fields from it. 39 | https://docs.veryfi.com/api/w-8ben-e/process-a-w-8-ben-e/ 40 | 41 | :param file_path: Path on disk to a file to submit for data extraction 42 | :param file_name: Optional name of file, eg. receipt.jpg 43 | :param kwargs: Additional body parameters 44 | :return: Data extracted from the w8. 45 | """ 46 | endpoint_name = "/w-8ben-e/" 47 | if file_name is None: 48 | file_name = os.path.basename(file_path) 49 | with open(file_path, "rb") as image_file: 50 | base64_encoded_string = base64.b64encode(image_file.read()).decode("utf-8") 51 | request_arguments = { 52 | "file_name": file_name, 53 | "file_data": base64_encoded_string, 54 | } 55 | request_arguments.update(kwargs) 56 | return self.client._request("POST", endpoint_name, request_arguments) 57 | 58 | def get_w8(self, document_id: int, **kwargs) -> Dict: 59 | """ 60 | Get W-8 endpoint allows you to retrieve a previously processed W-8 61 | https://docs.veryfi.com/api/w-8ben-e/get-a-w-8-ben-e/ 62 | 63 | :param document_id: The unique identifier of the document. 64 | :param kwargs: Additional query parameters 65 | :return: Document Data 66 | """ 67 | endpoint_name = f"/w-8ben-e/{document_id}/" 68 | return self.client._request("GET", endpoint_name, {}, kwargs) 69 | 70 | def get_w8s( 71 | self, 72 | created_date_gt: Optional[str] = None, 73 | created_date_gte: Optional[str] = None, 74 | created_date_lt: Optional[str] = None, 75 | created_date_lte: Optional[str] = None, 76 | **kwargs, 77 | ): 78 | """ 79 | Get list of w8s documents 80 | https://docs.veryfi.com/api/w-8ben-e/get-w-8-ben-es/ 81 | 82 | :param created_date__gt: Search for w8s documents with a created date greater than this one. Format YYYY-MM-DD+HH:MM:SS. Don't send both created_date__gt and created_date__gte in a single request. 83 | :param created_date__gte: Search for w8s documents with a created date greater than or equal to this one. Format YYYY-MM-DD+HH:MM:SS. Don't send both created_date__gt and created_date__gte in a single request. 84 | :param created_date__lt: Search for w8s documents with a created date greater than this one. Format YYYY-MM-DD+HH:MM:SS. Don't send both created_date__lt and created_date__lte in a single request. 85 | :param created_date__lte: Search for w8s documents with a created date less than or equal to this one. Format YYYY-MM-DD+HH:MM:SS. Don't send both created_date__lt and created_date__lte in a single request. 86 | :param kwargs: Additional query parameters 87 | :return: List of previously processed documents 88 | """ 89 | query_params = {} 90 | if created_date_gt: 91 | query_params["created_date__gt"] = created_date_gt 92 | if created_date_gte: 93 | query_params["created_date__gte"] = created_date_gte 94 | if created_date_lt: 95 | query_params["created_date__lt"] = created_date_lt 96 | if created_date_lte: 97 | query_params["created_date__lte"] = created_date_lte 98 | query_params.update(kwargs) 99 | 100 | endpoint_name = "/w-8ben-e/" 101 | return self.client._request("GET", endpoint_name, {}, query_params) 102 | 103 | def delete_w8(self, document_id): 104 | """ 105 | Delete Document from Veryfi 106 | https://docs.veryfi.com/api/w-8ben-e/delete-a-w-8-ben-e/ 107 | 108 | :param document_id: ID of the document you'd like to delete 109 | """ 110 | endpoint_name = f"/w-8ben-e/{document_id}/" 111 | self.client._request("DELETE", endpoint_name, {}) 112 | -------------------------------------------------------------------------------- /veryfi/w9s.py: -------------------------------------------------------------------------------- 1 | import os 2 | import base64 3 | from typing import Dict, Optional 4 | 5 | from veryfi.client_base import Client 6 | 7 | 8 | class W9s: 9 | def __init__(self, client: Client): 10 | self.client = client 11 | 12 | def process_w9_document_url( 13 | self, file_url: str, file_name: Optional[str] = None, **kwargs 14 | ) -> Dict: 15 | """ 16 | Process W9 Document from url and extract all the fields from it. 17 | https://docs.veryfi.com/api/w9s/process-a-w-9/ 18 | 19 | :param file_url: Publicly accessible URL to a file, e.g. "https://cdn.example.com/receipt.jpg". 20 | :param file_name: Optional name of file, eg. receipt.jpg 21 | :param kwargs: Additional request parameters 22 | :return: Data extracted from the document. 23 | """ 24 | if file_name is None: 25 | file_name = os.path.basename(file_url) 26 | endpoint_name = "/w9s/" 27 | request_arguments = { 28 | "file_name": file_name, 29 | "file_url": file_url, 30 | } 31 | request_arguments.update(kwargs) 32 | return self.client._request("POST", endpoint_name, request_arguments) 33 | 34 | def process_w9_document( 35 | self, file_path: str, file_name: Optional[str] = None, **kwargs 36 | ) -> Dict: 37 | """ 38 | Process W9 Document from url and extract all the fields from it. 39 | https://docs.veryfi.com/api/w9s/process-a-w-9/ 40 | 41 | :param file_path: Path on disk to a file to submit for data extraction 42 | :param file_name: Optional name of file, eg. receipt.jpg 43 | :param kwargs: Additional request parameters 44 | :return: Data extracted from the document. 45 | """ 46 | endpoint_name = "/w9s/" 47 | if file_name is None: 48 | file_name = os.path.basename(file_path) 49 | with open(file_path, "rb") as image_file: 50 | base64_encoded_string = base64.b64encode(image_file.read()).decode("utf-8") 51 | request_arguments = { 52 | "file_name": file_name, 53 | "file_data": base64_encoded_string, 54 | } 55 | request_arguments.update(kwargs) 56 | return self.client._request("POST", endpoint_name, request_arguments) 57 | 58 | def get_w9(self, document_id: int, **kwargs) -> Dict: 59 | """ 60 | Get a W-9 endpoint allows you to retrieve a previously processed W-9 61 | https://docs.veryfi.com/api/w9s/get-a-w-9/ 62 | 63 | :param document_id: The unique identifier of the document. 64 | :param bounding_boxes: A field used to determine whether or not to return bounding_box and bounding_region for extracted fields in the Document response. 65 | :param confidence_details: A field used to determine whether or not to return the score and ocr_score fields in the Document response. 66 | :return: Document Data 67 | """ 68 | endpoint_name = f"/w9s/{document_id}/" 69 | return self.client._request("GET", endpoint_name, {}, kwargs) 70 | 71 | def get_w9s( 72 | self, 73 | created_date_gt: Optional[str] = None, 74 | created_date_gte: Optional[str] = None, 75 | created_date_lt: Optional[str] = None, 76 | created_date_lte: Optional[str] = None, 77 | **kwargs, 78 | ): 79 | """ 80 | Get list of w9s documents 81 | https://docs.veryfi.com/api/w9s/get-w-9-s/ 82 | 83 | :param created_date__gt: Search for w9s documents with a created date greater than this one. Format YYYY-MM-DD+HH:MM:SS. Don't send both created_date__gt and created_date__gte in a single request. 84 | :param created_date__gte: Search for w9s documents with a created date greater than or equal to this one. Format YYYY-MM-DD+HH:MM:SS. Don't send both created_date__gt and created_date__gte in a single request. 85 | :param created_date__lt: Search for w9s documents with a created date greater than this one. Format YYYY-MM-DD+HH:MM:SS. Don't send both created_date__lt and created_date__lte in a single request. 86 | :param created_date__lte: Search for w9s documents with a created date less than or equal to this one. Format YYYY-MM-DD+HH:MM:SS. Don't send both created_date__lt and created_date__lte in a single request. 87 | :param kwargs: Additional request parameters 88 | :return: List of previously processed documents 89 | """ 90 | query_params = {} 91 | if created_date_gt: 92 | query_params["created_date__gt"] = created_date_gt 93 | if created_date_gte: 94 | query_params["created_date__gte"] = created_date_gte 95 | if created_date_lt: 96 | query_params["created_date__lt"] = created_date_lt 97 | if created_date_lte: 98 | query_params["created_date__lte"] = created_date_lte 99 | query_params.update(kwargs) 100 | 101 | endpoint_name = "/w9s/" 102 | return self.client._request("GET", endpoint_name, {}, query_params) 103 | 104 | def delete_w9(self, document_id: int): 105 | """ 106 | Delete a W-9 document. 107 | https://docs.veryfi.com/api/w9s/delete-a-w-9/ 108 | 109 | :param document_id: The unique identifier of the document. 110 | """ 111 | endpoint_name = f"/w9s/{document_id}/" 112 | self.client._request("DELETE", endpoint_name, {}) 113 | --------------------------------------------------------------------------------