Skip to content

Register a project

Prerequisites: an active, verified developer enrollment, the worker uploaded, and a manifest. The examples use the necter_dev.py helper from Sign in and enroll.

  1. Validate with prepare. Send the unsigned manifest. Set economics.vault to anything first; the response tells you the real predicted vault address.

    import json
    from necter_dev import api, login, personal_sign, sign_typed
    tok = login()
    manifest = json.load(open("manifest.json"))
    p = api("POST", "/v1/developers/projects/prepare", tok, json={"manifest": manifest})
    if p.get("vault"):
    manifest["consensus"]["economics"]["vault"] = p["vault"]
    p = api("POST", "/v1/developers/projects/prepare", tok, json={"manifest": manifest})
    for problem in p["problems"]:
    print(problem["path"], problem["code"], problem["message"])
    assert p["valid"]

    A valid response carries canonical (the exact UTF-8 text to sign), manifest_hash, consensus_hash, project_id, vault and tier_changes.

  2. Sign the canonical bytes with the developer wallet (personal_sign, EIP-191) and build the envelope:

    envelope = {
    "manifest": manifest,
    "manifest_hash": p["manifest_hash"],
    "consensus_hash": p["consensus_hash"],
    "project_id": p["project_id"],
    "sig_type": "eip191",
    "signature": personal_sign(p["canonical"]),
    }
  3. Submit it.

    r = api("POST", "/v1/developers/projects", tok, json=envelope)
    project_id = r["project"]["project_id"]

    The response holds register_tx (an ordinary transaction you could send yourself) and register_by_sig (a gasless payload, valid for one hour).

  4. Register on-chain without gas. Sign the EIP-712 Register typed data and post it back; the Hub’s relayer calls ProjectRegistry.registerBySig.

    td = r["register_by_sig"]["typed_data"]
    status = api("POST", f"/v1/developers/projects/{project_id}/register", tok,
    json={"typed_data": td, "signature": sign_typed(td)})
    print(status) # {"status": "queued" | "sent" | "confirmed", "tx_hash": "0x…"}

    If the hour passed, get fresh payloads with GET /v1/developers/projects/{project_id}/register-payload.

  5. Wait for indexing and review. Once the Hub indexes ProjectRegistered, the project moves to pending_review. Check with GET /v1/projects/{project_id} (listing_status) or GET /v1/developers/me/projects. On the testnet the network operator reviews and lists it.

Meanwhile, create and fund the vault: Vault, reward token, funding. You can also run up to 10 sample tasks through validators before the listing goes live:

sim = api("POST", f"/v1/developers/projects/{project_id}/simulations", tok,
json={"tasks": [{"function": "work", "input": {"seed": "0x" + "ab" * 32, "seq": 0}}]})
print(api("GET", f"/v1/developers/projects/{project_id}/simulations/{sim['simulation_id']}", tok))
Status / error Cause
422 invalid_manifest A field breaks a rule; details.problems lists each one with its path
422 invalid_manifest “a new project starts at version 1” version must be 1 and previous null for a new project
409 already_exists This developer already has a project with this slug
403 not_enrolled / 403 not_verified Finish enrollment first
409 invalid_state “project is already registered on chain” Registration already went through
400 on /register The typed data was not signed by the developer, or the payload expired