# ABN Lookup — App Manual

## Contract

- App ID: `abn_lookup_app`
- App version: `1.0.0`
- Platform contract: `1.0`
- Provider: Australian Business Register (ABR)

The machine-readable source of truth is `APP_MANIFEST` in `__init__.py`.

## Credential

`abr_lookup_guid` — ABR authentication GUID. It is entered once through the
WordPress Integrations screen, encrypted in the tenant vault, and never returned
to WordPress.

Credential states:

- **Not configured**: no encrypted value exists.
- **Configured**: encrypted value exists; provider has not necessarily accepted it.
- **Connected**: `platform.credentials.test` completed a real ABR request.

## Exported capabilities

### `platform.health`

Action: `health_v1`

No external request. Returns local health, vault readiness, and whether the
credential exists. `provider_checked` is always false.

### `platform.credentials.test`

Action: `test_connection_v1`

Optional input:

```json
{"probe_abn": "51824753556"}
```

Performs a minimal ABR lookup and reports whether the provider connection
succeeded. It never returns the GUID.

### `business.au.abn.lookup`

Action: `lookup_v1`

Input:

```json
{"abn": "51824753556", "include_history": false}
```

Success data:

```json
{
  "abn": "51824753556",
  "record": {
    "abn": "51824753556",
    "entity_name": "..."
  }
}
```

### Agent tool

This capability is agent-ready but does not depend on an AI runtime:

- Tool capability: `business.au.abn.lookup`
- Exposure: enabled
- Risk: low / read-only
- Side effects: none
- Confirmation: not required
- Required permission: `abn_lookup.read`
- Use when an exact 11-digit ABN is available or an Australian registration
  must be validated.
- Do not use for business-name-only searches or non-Australian entities.

The agent receives only the capability schemas and semantic metadata. It never
receives the app source code, ABR GUID, provider implementation, or tenant
storage. Inputs remain subject to JSON Schema and the normal gateway checks.

Manual PHP, shortcode, HTTP, and app-to-app calls continue to use the same
`lookup_v1` implementation and return the same Standard v1 envelope.

## Error contract

| Code | HTTP | Retryable | Meaning |
|---|---:|---|---|
| `invalid_input` | 400 | No | Required input is absent. |
| `invalid_abn` | 400 | No | The ABN format is invalid. |
| `credential_not_configured` | 409 | No | The tenant has no ABR GUID. |
| `provider_unavailable` | 502 | Yes | ABR could not be reached. |
| `provider_invalid_response` | 502 | Yes | ABR returned an incomplete record. |
| `action_not_found` | 404 | No | The requested action is unsupported. |

Messages are safe for clients. Raw provider responses, exceptions, credentials,
stack traces, and internal paths are not returned.

## Calling from another app

The caller manifest must declare:

```python
outbound_capabilities=(
    "abn_lookup_app:business.au.abn.lookup",
)
```

## Calling through HTTP

Endpoint:

```text
POST https://<flask-host>/flask/api/v1/abn_lookup_app/lookup_v1
```

Headers:

```text
Authorization: Bearer <RS256 site JWT>
Content-Type: application/json
X-App-ID: abn_lookup_app
```

Body:

```json
{"abn": "51824753556", "include_history": false}
```

Example:

```bash
curl --request POST \
  --header "Authorization: Bearer $JWT" \
  --header "Content-Type: application/json" \
  --header "X-App-ID: abn_lookup_app" \
  --data '{"abn":"51824753556"}' \
  "https://<flask-host>/flask/api/v1/abn_lookup_app/lookup_v1"
```

The JWT must contain `app:abn_lookup_app:invoke` and identify the subscribed
tenant/site. Clients must not send the ABR GUID in this request.

Standard success envelope:

```json
{
  "contract_version": "1.0",
  "status": "ok",
  "app_id": "abn_lookup_app",
  "action": "lookup_v1",
  "request_id": "...",
  "data": {
    "abn": "51824753556",
    "record": {}
  },
  "meta": {
    "provider": "Australian Business Register"
  },
  "error": null
}
```

## WordPress App Pack shortcode

After installing and activating the optional `abn_lookup_wp` App Pack, add this
shortcode to a WordPress page:

```text
[ams_abn_lookup_card]
```

The shortcode is available to signed-in WordPress users. The App Pack calls the
public bridge helper; the bridge creates the short-lived RS256 JWT server-side,
calls `lookup_v1`, and returns normalized business fields. The browser never
receives the JWT or ABR credential.

This shortcode is a reference client, not a requirement. A custom WordPress
theme, React UI, mobile application, or another trusted server can use the same
HTTP contract.

## Calling from custom WordPress PHP

Custom plugins, theme templates, Gutenberg render callbacks, and server-side
WordPress code should use the bridge helper:

```php
$result = wp_flask_bridge_invoke(
    'abn_lookup_app',
    'lookup_v1',
    [
        'abn' => '51824753556',
        'include_history' => false,
    ]
);

if (is_wp_error($result)) {
    echo esc_html($result->get_error_message());
    return;
}

$record = $result['data']['record'];
echo esc_html($record['entity_name'] ?? 'Business found');
```

The helper:

- creates a new short-lived RS256 JWT for every request;
- derives the Flask URL and required app scope from bridge configuration;
- sends JSON and required headers server-side;
- returns the complete Standard v1 envelope on success;
- converts HTTP and contract errors into `WP_Error`.

Do not call the private bridge class, read its options, construct JWTs, or place
credentials in theme code. The public compatibility surface is
`wp_flask_bridge_invoke()`.

WordPress does not execute PHP pasted into the block editor. Put custom PHP in
a child-theme template, a small custom plugin, a server-side block render
callback, or a controlled snippets tool. Do not enable arbitrary PHP execution
inside page content.

## Optional downloadable WordPress App Pack

Package ID: `abn_lookup_wp`

Installed test release: `1.1.0-dev`

Available signed portable release: `1.1.1-dev`

Source:

```text
wordpress-pack/abn-lookup-wp/
```

The reference pack provides:

```php
$record = ams_abn_lookup_record('51824753556');
```

and:

```text
[ams_abn_lookup_card]
```

Releases `1.1.0-dev` and later also provide:

```text
[ams_abn_status_badge abn="78389938080"]
```

and:

```php
$active = ams_abn_lookup_is_active('78389938080');
```

The pack is optional. Custom PHP can always use `wp_flask_bridge_invoke()`
directly. The current reproducible, Ed25519-signed development release is
available from **WP Flask Bridge → Apps** only when `abn_lookup_app` is included
in the site's effective subscription. Select **Refresh app downloads**, then
**Download & install tools**. Bridge Core downloads it server-side, verifies
its size, SHA-256 digest and Ed25519 signature, validates the ZIP, and installs
it inactive. Activation remains an explicit WordPress administrator action.

### Signed update test

1. Keep App Pack `1.1.0-dev` active.
2. Visit any WordPress administrator screen and confirm the signed-update
   notice.
3. Open **WP Flask Bridge → Apps**.
4. Confirm `1.1.0-dev → 1.1.1-dev` and review the release notes.
5. Select **Install signed update**.
6. Confirm the App Pack remains active and reports version `1.1.1-dev`.
7. Confirm the existing `[ams_abn_lookup_card]` still works.
8. Add `[ams_abn_status_badge abn="78389938080"]` to the test page and confirm
   the compact status appears.

If WordPress reports that it cannot install a verified package, first inspect
the upgrader error and plugin-directory ownership. This development site's
`1.0.1-dev` package was initially installed through WP-CLI as `cgarcia`, while
web updates run as `nobody`; the signed artifact verified correctly but Apache
could not replace those CLI-owned files. Production must use a consistent
deploy identity or shared web/deploy group rather than globally writable
permissions.

Bridge Core refreshes the entitlement-aware catalog twice daily and also when
an administrator opens the dashboard after the cached catalog becomes stale.
It verifies compatibility, size, SHA-256, Ed25519 signature, ZIP safety,
package identity, version, capabilities, secret declaration, manifest, and
entrypoint before WordPress overwrites plugin code. WordPress options and
external customizations are not stored in the App Pack directory and therefore
remain intact.

Release `1.1.1-dev` is the first portable update release that signs the generic
WordPress `plugin_file`. Bridge Core negotiates Direct, FTP, FTPS, or SSH through
WordPress's native filesystem API and does not store those credentials. Updates
use WordPress temporary backups for automatic failure restoration. Manual
rollback is offered only to an earlier non-revoked signed release that also
contains portable identity metadata.

## Security and tenant isolation

- The tenant ID comes from authenticated gateway context, never from input.
- The ABR GUID is resolved inside this app from the tenant vault.
- WordPress and other callers send only business input.
- The app cannot read another tenant's secrets or storage namespace.
- The App Pack uses only the public bridge helper and declared capability.
- Logs redact credentials, JWTs, signing keys, provider payloads, and full ABNs.

## Observability

The App Pack shows one explicit outcome after every submitted lookup:

- **Lookup completed successfully** with normalized business fields.
- **Lookup failed** with a safe user-facing message.

Both outcomes include the Standard v1 `request_id` as a support reference when
available. Platform operators can correlate it with the structured technical
log:

```text
logs/bridge_platform/abn_lookup_app.log
```

The log records tenant, outcome, request ID, provider status and only the final
four ABN digits. It never records the ABR GUID, site JWT, signing keys, or
credential values.

## Testing the credential

In WordPress:

1. Open **WP Flask Bridge → Integrations**.
2. Save the ABR authentication GUID.
3. Enter an 11-digit test ABN.
4. Select **Test connection**.

The UI changes from **Configured** to **Connected** only after
`test_connection_v1` completes a real ABR lookup.

Call:

```python
call_capability(
    context,
    "abn_lookup_app",
    "business.au.abn.lookup",
    {"abn": supplier_abn},
)
```

## Testing and validation

Run:

```text
venv/bin/python scripts/validate_app_contract.py abn_lookup_app
venv/bin/python -m unittest discover -s tests/platform -p '*_checks.py'
```

The automated checks cover the manifest, envelopes, missing credentials,
vault-only credential resolution, exported capability, signed package,
tamper detection, entitlements, JWT authentication, quotas, storage, and tenant
secret isolation. Provider connectivity is tested separately through
`test_connection_v1` because it requires a real ABR request.

## Versioning and compatibility

- Flask app version: `1.0.0`.
- Platform contract: `1.0`.
- WordPress App Pack available test version: `1.1.1-dev`.
- Existing fields remain compatible within contract major `1`.
- Additive optional fields may be introduced in minor releases.
- Removing or changing required fields needs a new action/capability version.
- App Pack updates must be signed and may not overwrite customer customization
  stored outside the App Pack directory.

## Legacy compatibility

Actions `test` and `lookup` remain temporarily available for current callers.
They are not part of Standard v1 and will be removed after `wp_invoices` is
migrated.
