Contract summary
- Method and destination:
POSTto anhttpsURL on port 443 or 8443 with a public DNS name. - Body: a CloudEvents 1.0 envelope in JSON of up to 256 KiB.
- Signature: HMAC-SHA256 according to Standard Webhooks, present in every delivery with any authentication method.
- Success: any
2xxresponse. It means receipt, not business processing; respond in under 5 s and process asynchronously. - Time limit: 5 s to connect and 15 s in total, including DNS and TLS.
- Delivery: at least once. The receiver must be idempotent by
webhook-id.
Event types are in the event catalog and the shape of data in the JSON Schemas. API error codes are listed under Errors.
Envelope and formats
The body is serialized as canonical JSON: keys sorted at every level and no whitespace. The example below is the real output of the Masnet delivery code for a contract status change, with fictitious data, indented only for readability.
{
"data": {
"actor": {
"type": "user"
},
"changed_fields": [
"status"
],
"evidence": "masnet_record",
"object": {
"contract_code": "CT-0001",
"id": "2f0c7a2e-6b1d-4e57-9a40-1c3f5d2b8e91",
"status": "active",
"tender": {
"code": "1234-56-LE26"
},
"version": 14
},
"previous": {
"status": "pending_formalization"
}
},
"datacontenttype": "application/json",
"dataschema": "https://masnet.com/developers/schemas/contract/v1.json",
"environment": "test",
"id": "evt_01JBE6Q0Z8B3V4R7T2M9K1N5XH",
"sequence": 14,
"source": "https://daas.masnet.com/api/v1/integrations/companies/0b9e3c1a-7d42-4f0e-8a51-6c2b9d4e7f10",
"specversion": "1.0",
"subject": "contracts/2f0c7a2e-6b1d-4e57-9a40-1c3f5d2b8e91",
"tenantid": "0b9e3c1a-7d42-4f0e-8a51-6c2b9d4e7f10",
"time": "2026-10-28T12:00:00.000Z",
"type": "com.masnet.contract.status_changed.v1"
}| Member | Meaning |
|---|---|
specversion, datacontenttype | 1.0 and application/json. |
id | Event identifier with the evt_ prefix. It is the same across retries and redeliveries and equals webhook-id. |
type | Type from the catalog, with the major version at the end (.v1). |
source | Origin of the company that emits the event. |
subject | Affected resource, in the form <type>/<identifier>. |
time | Instant of the fact in UTC. |
dataschema | JSON Schema of the data object: https://masnet.com/developers/schemas/<object>/v1.json. |
tenantid | Company identifier. |
sequence | Resource version after the change; it grows per subject. |
environment | production or test. |
data.object | Representation of the resource with the field groups granted to the destination (public, business, confidential, people) merged into one level. |
data.previous | Only the changed fields that the granted permission allows to see; may be null. |
data.changed_fields | Names of the fields that changed and that the destination can see. |
data.changed_groups | Only in catalog_item.updated: field groups that changed within the granted permission. |
data.actor | type plus, with the people group, the data of the human actor. |
data.evidence | masnet_record, mp_observed or mp_confirmed. |
data.truncated | Present if a collection of more than 200 items was cut or if the body would have exceeded 256 KiB. |
The destination chooses one of two formats with the same envelope: standard JSON (Content-Type: application/json) and structured CloudEvents (Content-Type: application/cloudevents+json). The audit.user_actions event is the exception: its data is { "batch": …, "actions": […] }, with no object; it is described in its schema. State enumerations are open: ignore values you do not know.
Headers of each delivery
| Header | Value |
|---|---|
Content-Type | application/json or application/cloudevents+json, according to the destination format. |
User-Agent | Masnet-Webhooks/1.0 (+https://masnet.com/developers/webhooks) |
webhook-id | Event identifier (id of the body). Idempotency key. |
webhook-timestamp | Unix seconds of the attempt. It is signed together with the body. |
webhook-signature | One or two v1,<base64> signatures separated by a space. |
masnet-event-type | Event type, to route without reading the body. |
masnet-delivery-attempt | Attempt number, starting at 1. |
Depending on the authentication method chosen, an Authorization header or the API key header defined by the administrator is also added; see Authentication toward your receiver.
Signature verification
Masnet implements Standard Webhooks. The secret has the form whsec_ followed by 32 random bytes in base64; Masnet generates it and shows it only once.
- Read the body unmodified: the bytes received, not a re-serialization of the JSON.
- Reject the message if
webhook-timestampdiffers from your clock by more than 5 minutes (recommended tolerance). - Compute
HMAC-SHA256with the key equal to the base64 decoding of the secret without thewhsec_prefix, over the byteswebhook-id+.+webhook-timestamp+.+ body. - Encode the result in base64 and compare it, in constant time, with each signature of
webhook-signaturethat starts withv1,. One match is enough.
Implementations verified against vectors generated with the Masnet signing code are in the guides for Node.js, Python, Java and C#.
Secret rotation
In a planned rotation the new secret coexists for 24 hours with the previous one and each delivery carries both signatures in webhook-signature, separated by a space; the verifier must accept the message if either matches. If the administrator flags a compromise of the secret, the previous one is revoked immediately with no overlap.
webhook-signature: v1,S01nQT5xCo7TbAw+szB3vn4tFuJWRq+1Kzf/RQ9r5u4= v1,xcJ6+lQM63rZrPcdOoFUO1JJJYzxs1QY1uW1vUTyp6g=Responses and retries
| Receiver response | Effect in Masnet |
|---|---|
2xx | Successful delivery. At most 2 KiB of the response is read, for diagnostics only. |
3xx | Not followed. It counts as a failure and is retried; the history records the location without user, password, query or fragment. |
410 Gone | Final: the destination is disabled and its open deliveries are cancelled. |
413 | Final for that event: it is marked undeliverable to that destination and is not retried. |
429 and 503 | Retried. If it carries Retry-After (seconds or HTTP date), the destination is paused until that time, up to a maximum of 1 hour. |
| Any other code, timeout, connection or TLS error | Retried according to the schedule. |
The schedule offers up to 15 attempts, measured from the start of the cycle: immediate, 1 min, 5 min, 15 min, 30 min, 1 h, 2 h, 4 h, 8 h, 12 h, 24 h, 36 h, 48 h, 60 h and 72 h. No attempt happens after 72 hours from the start of the cycle; if exhausted, the delivery is marked failed. A redelivery from the application opens a new cycle with the same id.
Connecting has a 5 s limit and the complete request 15 s. With an OAuth 2.0 authentication method, on a 401 Masnet renews the token once and repeats the delivery within the same attempt.
A body that still exceeds 256 KiB after truncating collections is not sent and the delivery is marked failed for size.
Idempotency, ordering and duplicates
- Delivery is at least once: store
webhook-idand discard repeats before producing effects. sequenceis the resource version and grows persubject. Discard an event whosesequenceis lower than or equal to the last one you applied for thatsubject.- The administrator can enable per-resource ordered delivery on the destination: each
subjectthen has at most one delivery in progress and the following ones wait. Without that option, order is reconstructed withsequence. - A redelivery of an old event may arrive after a newer one;
sequencelets you discard it.
Authentication toward your receiver
The signature is always present. In addition, the destination can present a credential. It is configured in the application, per destination.
| Method | What Masnet sends |
|---|---|
| Signature only | Only the signature headers. |
| API key header | The header with the name you define (letters, digits and hyphens, 1 to 64 characters) and its value. It cannot replace Host, Content-Type, Content-Length, User-Agent, Authorization, Cookie, Connection, Transfer-Encoding, Proxy-Authorization, webhook-id, webhook-timestamp, webhook-signature, masnet-event-type or masnet-delivery-attempt. |
| Basic | Authorization: Basic with the user name and password. |
| Static Bearer | Authorization: Bearer with the token. |
| OAuth 2.0, client credentials | Masnet requests a token from your authorization server with grant_type=client_credentials and sends Authorization: Bearer. The token is kept in memory up to 80 % of its lifetime (300 s if expires_in is not reported; maximum 24 h). |
| mTLS | A client certificate issued by Masnet for your destination. |
OAuth 2.0 with client secret
Masnet authenticates the client with client_secret_basic (default) or client_secret_post, and can send scope and audience. The authorization server must respond 200 with access_token and, if it reports token_type, it must be Bearer.
OAuth 2.0 with private_key_jwt
Without a client secret: Masnet signs a JWT assertion (RFC 7523) with a key of its own for the destination, with iss and sub equal to the client_id, aud equal to the token endpoint, a lifetime of 60 s and a unique jti. The algorithms are RS256 and ES256. The public key is published in two ways, depending on what your authorization server supports:
- JWKS, for Okta, Auth0, Ping and similar:
https://daas.masnet.com/api/v1/integrations/public/endpoints/{id}/jwks.json. Each key includesx5c. - Self-signed X.509 certificate with the same key, for Microsoft Entra ID:
https://daas.masnet.com/api/v1/integrations/public/endpoints/{id}/oauth-cert.pem. The assertion carriesx5t#S256andx5t. The certificate is valid for 730 days.
{id} is the destination identifier shown by the application. When the key is rotated a new key and certificate are generated, and you must upload the new certificate to your authorization server within the 24-hour overlap.
mTLS
Masnet presents one client certificate per destination. For your receiver to validate it, trust the certificate authority published at https://daas.masnet.com/api/v1/integrations/public/mtls-ca.pem. Certificates are renewed before they expire and during the overlap the destination keeps both.
Destination URL and credentials in the query
The URL must use https, port 443 or 8443 and a public DNS name; user info in the URL, fragments, literal IP addresses and internal names are not accepted. A query is accepted: platforms such as Azure Logic Apps and Power Automate sign the URL with a sig parameter. Masnet treats the entire URL as a credential: it encrypts it like a secret, the application shows only the host and path with the query masked, and it does not appear in logs or metrics. Changing the URL creates a new generation of the destination: it pauses the destination, cancels what is pending from the previous generation and requires a successful test.
Test events
A destination must pass a test before it receives real events. The test sends the type com.masnet.integration.test.v1 with the same signature, authentication and network validation as a real event. Its environment is test, its subject is integration/<destination> and its data.object is { "message": "Evento de prueba de Masnet", "test": true }. It requires no data permission. The schema is at /developers/schemas/integration/v1.json.
Recovery by reading
If the receiver was down for more than 72 hours or cannot expose a public endpoint, the same envelopes are available in the events API (https://daas.masnet.com/api/v1/integrations/data/events) for 30 days.