Mono Colombia

Target discovery

Find a beneficiary's Bre-B key from identity and bank-account data before sending money.

Use target discovery when you know the beneficiary's identity and destination account but do not know their exact Bre-B key. Mono tests a bounded set of keys derived from the data you provide and returns a usable target only when it can confirm the same person, bank, and account.

If you already know the exact key, call Resolve target instead. Discovery is for finding a key from beneficiary data, not for listing every key a person owns.

Testing in sandbox

Use the deterministic 101104 document suffixes to exercise exact, alternative, cached-empty, and unavailable outcomes. See target discovery in sandbox.

Before you start

  • Authenticate with OAuth 2.0 client credentials.
  • Confirm that Mono has enabled target discovery for your tenant.
  • Use target_discoveries for POST /api/v1/targets/discover.
  • Use either target_discoveries or target_discoveries:readonly for the bank catalog GET endpoints.
  • Have the tenant_account_id that will originate the outgoing transfer. It must belong to the authenticated tenant.

See Bre-B Participant authentication for the token flow.

1. Select the canonical bank code

Discovery accepts a three-digit Colombian ACH code in account.bank_code. Do not derive that code from a bank name yourself. Search the catalog first:

GET /api/v1/banks?q=bancolombia&limit=5

An exact ACH code, canonical name, or alias produces a confident match and no suggestions:

{
  "catalog_version": 1,
  "match": {
    "ach_code": "007",
    "name": "BANCOLOMBIA"
  },
  "data": []
}

A partial or approximate query produces suggestions instead:

GET /api/v1/banks?q=davi&limit=5
{
  "catalog_version": 1,
  "match": null,
  "data": [
    { "ach_code": "051", "name": "DAVIVIENDA" },
    { "ach_code": "551", "name": "DAVIPLATA" }
  ]
}

Only a non-null match is safe to select automatically. Every item in data is a suggestion—even when there is only one—so show the choices to the payer and use the ach_code they select. A search with no results is still 200, with match: null and data: [].

You can also list the entire catalog or look up an exact code. See List banks and Get bank.

2. Discover the target

Send the beneficiary data you already know. Names, phone, and email are optional hints; identity, bank, and account are required.

POST /api/v1/targets/discover
Content-Type: application/json
Authorization: Bearer <access_token>
{
  "tenant_account_id": "bbtacc_5tgliBmzjZ6mpQPRbQjfKj",
  "person": {
    "document_type": "CC",
    "document_number": "1192793827",
    "first_name": "Diego",
    "middle_name": null,
    "first_surname": "Sánchez",
    "second_surname": null,
    "phone": "+573104838791",
    "email": "diego@example.com"
  },
  "account": {
    "bank_code": "013",
    "number": "0013123456789",
    "type": "savings_account"
  }
}

See Discover target for every field and enum.

3. Handle the result

Both normal outcomes return 200. Check state; do not treat every 200 as a discovered target.

stateMeaning
discoveredtarget and resolution_id are present. The target matches the requested person, bank, and account.
not_discoveredtarget and resolution_id are null. state_reason is no_exact_target; this does not mean the person has no other key.

Exact target found

{
  "state": "discovered",
  "state_reason": null,
  "attempts_count": 2,
  "resolution_id": "bbtgr_5tgliBmzjZ6mpQPRbQjfKj",
  "target": {
    "id": "bbtgt_5tgliBmzjZ6mpQPRbQjfKj",
    "key_type": "identification",
    "key_value": "1192793827",
    "resolution_type": "plain_key",
    "spbvi": "CRB",
    "participant_nit": "860003020",
    "creditor": {
      "type": "natural",
      "document_type": "CC",
      "document_number": "1192793827",
      "full_name": "Diego Sánchez"
    },
    "creditor_account": {
      "type": "savings_account",
      "number": "0013123456789",
      "currency_code": "COP"
    },
    "transaction_amount": null,
    "payment_id": null
  },
  "same_person_keys": [
    {
      "key_type": "alphanumeric",
      "key_value": "@BBVA3104838791",
      "bank_code": "007",
      "participant_nit": "890903938",
      "creditor_account": {
        "type": "savings_account",
        "number_last_four": "4321",
        "currency_code": "COP"
      },
      "resolution_required": true
    }
  ]
}

The target is fresh and single-use: pass its id to exactly one outgoing transfer. Calling discovery again creates another target rather than returning a cached target.

No exact target found

{
  "state": "not_discovered",
  "state_reason": "no_exact_target",
  "attempts_count": 4,
  "resolution_id": null,
  "target": null,
  "same_person_keys": [
    {
      "key_type": "email",
      "key_value": "diego@example.com",
      "bank_code": "007",
      "participant_nit": "890903938",
      "creditor_account": {
        "type": "savings_account",
        "number_last_four": "4321",
        "currency_code": "COP"
      },
      "resolution_required": true
    }
  ]
}

same_person_keys is always present and may be empty. It contains at most five keys confirmed for the same identity, never data from a different person. It is not exhaustive: candidates come only from the request data, and discovery stops at the first exact target. Therefore, a discovered response includes only same-person keys observed before that first exact target.

An item in same_person_keys is not a target. Before using one:

  1. Let the payer choose the alternative.
  2. Call Resolve target with its key_value to obtain a fresh target.
  3. Confirm that the fresh target's participant_nit and the ending of creditor_account.number still match the alternative's participant_nit and number_last_four.
  4. Ask for confirmation before sending money.

The extra resolution is required because an alternative is not reserved and may point somewhere else later.

Cache behavior

Mono may cache only not_discovered results that have an empty same_person_keys list, for up to one hour. On such a cache hit, attempts_count is 0. Results containing a target or an alternative are not served from cache, so their directory data and IDs come from the current request.

Send the outgoing transfer

After the payer confirms the resolved beneficiary, create the outgoing transfer with the target ID:

{
  "tenant_account_id": "bbtacc_5tgliBmzjZ6mpQPRbQjfKj",
  "description": "Confirmed beneficiary payout",
  "transfers": [
    {
      "target_id": "bbtgt_5tgliBmzjZ6mpQPRbQjfKj",
      "amount": { "amount": 12000000, "currency": "COP" },
      "external_id": "confirmed-transfer-1"
    }
  ]
}

See Create outgoing transfers for the complete request. A target can fund only one transfer; use a new discovery or resolution for another payment.

When discovery creates an exact target, the existing target_resolution_created and target_resolution_resolved webhooks apply. not_discovered results and alternatives do not emit target-resolution webhooks. Consumers should deduplicate resolution webhooks by resolution_id; delivery is asynchronous, so a target_resolution_created event may already carry state=resolved and ordering is not guaranteed. Treat the latest state and updated_at as authoritative.

Errors and retries

StatusWhen it occursWhat to do
400The document or account number cannot be compared safely.Correct the beneficiary data; do not retry unchanged.
401The token is missing, invalid, or expired.Obtain a valid token and retry.
403The credential lacks the required scope, or discovery is not enabled for the tenant.Request the scope or contact Mono to enable target discovery.
404The tenant account does not exist or does not belong to the tenant.Verify tenant_account_id. An unknown exact bank lookup also returns bank_not_found.
422The payload is invalid or bank_code is not accepted (unknown_bank).Correct the request or select a bank from the public catalog.
429The per-client or per-tenant discovery limit was exceeded (rate_limit_exceeded).Wait for the Retry-After interval before retrying.
503The Bre-B directory is temporarily unavailable.Retry after Retry-After when present, using bounded backoff.
500An unexpected server error occurred.Retry safely; contact support with the response id if it persists.

A valid search with no exact target is not an HTTP error: it returns 200 with state=not_discovered.

Next steps

On this page